@screenbook/cli 1.1.3 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["CONFIG_FILES","duplicateIds: string[]","cycles: CycleInfo[]","path: string[]","current: string | null | undefined","lines: string[]","errors: ValidationError[]","bestMatch: string | undefined","matrix: number[][]","lines: string[]","screens: ScreenWithFilePath[]","routeFiles: string[]","missing: CoverageData[\"missing\"]","byOwner: CoverageData[\"byOwner\"]","byTag: CoverageData[\"byTag\"]","lines: string[]","screens: ScreenWithFilePath[]","results: CheckResult[]","summary: string[]","transitive: TransitiveDependency[]","queue: Array<{ id: string; path: string[] }>","lines: string[]","screens: Screen[]","FRAMEWORKS: FrameworkDefinition[]","framework: FrameworkInfo | null","missingMeta: string[]","covered: string[]","invalid: InvalidNavigation[]","orphans: Screen[]","lines: string[]","changedFiles: string[]","screens: Screen[]","results: ImpactResult[]"],"sources":["../src/utils/config.ts","../src/utils/cycleDetection.ts","../src/utils/errors.ts","../src/utils/logger.ts","../src/utils/validation.ts","../src/commands/build.ts","../src/commands/dev.ts","../src/commands/doctor.ts","../src/commands/generate.ts","../src/utils/impactAnalysis.ts","../src/commands/impact.ts","../src/utils/detectFramework.ts","../src/commands/init.ts","../src/commands/lint.ts","../src/utils/prImpact.ts","../src/commands/pr-impact.ts","../src/index.ts"],"sourcesContent":["import { existsSync } from \"node:fs\"\nimport { resolve } from \"node:path\"\nimport { type Config, defineConfig } from \"@screenbook/core\"\nimport { createJiti } from \"jiti\"\n\nconst CONFIG_FILES = [\n\t\"screenbook.config.ts\",\n\t\"screenbook.config.js\",\n\t\"screenbook.config.mjs\",\n]\n\nexport async function loadConfig(configPath?: string): Promise<Config> {\n\tconst cwd = process.cwd()\n\n\t// If config path is provided, use it\n\tif (configPath) {\n\t\tconst absolutePath = resolve(cwd, configPath)\n\t\tif (!existsSync(absolutePath)) {\n\t\t\tthrow new Error(`Config file not found: ${configPath}`)\n\t\t}\n\t\treturn await importConfig(absolutePath, cwd)\n\t}\n\n\t// Search for config file in cwd\n\tfor (const configFile of CONFIG_FILES) {\n\t\tconst absolutePath = resolve(cwd, configFile)\n\t\tif (existsSync(absolutePath)) {\n\t\t\treturn await importConfig(absolutePath, cwd)\n\t\t}\n\t}\n\n\t// Return default config if no config file found\n\treturn defineConfig()\n}\n\nasync function importConfig(\n\tabsolutePath: string,\n\tcwd: string,\n): Promise<Config> {\n\tconst jiti = createJiti(cwd)\n\tconst module = (await jiti.import(absolutePath)) as { default?: Config }\n\n\tif (module.default) {\n\t\treturn module.default\n\t}\n\n\tthrow new Error(`Config file must have a default export: ${absolutePath}`)\n}\n","import type { Screen } from \"@screenbook/core\"\n\n/**\n * Information about a detected cycle\n */\nexport interface CycleInfo {\n\t/**\n\t * Array of screen IDs forming the cycle, including the repeated start node\n\t * @example [\"A\", \"B\", \"C\", \"A\"]\n\t */\n\tcycle: string[]\n\t/**\n\t * True if any screen in the cycle has allowCycles: true\n\t */\n\tallowed: boolean\n}\n\n/**\n * Result of cycle detection analysis\n */\nexport interface CycleDetectionResult {\n\t/**\n\t * True if any cycles were detected\n\t */\n\thasCycles: boolean\n\t/**\n\t * All detected cycles\n\t */\n\tcycles: CycleInfo[]\n\t/**\n\t * Cycles that are not allowed (allowed: false)\n\t */\n\tdisallowedCycles: CycleInfo[]\n\t/**\n\t * Screen IDs that appear more than once (indicates data issue)\n\t */\n\tduplicateIds: string[]\n}\n\n// Node colors for DFS\nenum Color {\n\tWhite = 0, // Unvisited\n\tGray = 1, // In progress (on current path)\n\tBlack = 2, // Completed\n}\n\n/**\n * Detect circular navigation dependencies in screen definitions.\n * Uses DFS with coloring algorithm: O(V + E) complexity.\n *\n * @example\n * ```ts\n * const screens = [\n * { id: \"A\", next: [\"B\"] },\n * { id: \"B\", next: [\"C\"] },\n * { id: \"C\", next: [\"A\"] }, // Creates cycle A → B → C → A\n * ]\n * const result = detectCycles(screens)\n * // result.hasCycles === true\n * // result.cycles[0].cycle === [\"A\", \"B\", \"C\", \"A\"]\n * ```\n */\nexport function detectCycles(screens: Screen[]): CycleDetectionResult {\n\tconst screenMap = new Map<string, Screen>()\n\tconst duplicateIds: string[] = []\n\n\t// Build screen map and detect duplicates\n\tfor (const screen of screens) {\n\t\tif (!screen.id || typeof screen.id !== \"string\") {\n\t\t\t// Skip screens with invalid IDs\n\t\t\tcontinue\n\t\t}\n\t\tif (screenMap.has(screen.id)) {\n\t\t\tduplicateIds.push(screen.id)\n\t\t}\n\t\tscreenMap.set(screen.id, screen)\n\t}\n\n\tconst color = new Map<string, Color>()\n\tconst parent = new Map<string, string | null>()\n\tconst cycles: CycleInfo[] = []\n\n\t// Initialize all nodes as white\n\tfor (const id of screenMap.keys()) {\n\t\tcolor.set(id, Color.White)\n\t}\n\n\t// DFS from each unvisited node\n\tfor (const id of screenMap.keys()) {\n\t\tif (color.get(id) === Color.White) {\n\t\t\tdfs(id, null)\n\t\t}\n\t}\n\n\tfunction dfs(nodeId: string, parentId: string | null): void {\n\t\tcolor.set(nodeId, Color.Gray)\n\t\tparent.set(nodeId, parentId)\n\n\t\tconst node = screenMap.get(nodeId)\n\t\tconst neighbors = node?.next ?? []\n\n\t\tfor (const neighborId of neighbors) {\n\t\t\tconst neighborColor = color.get(neighborId)\n\n\t\t\tif (neighborColor === Color.Gray) {\n\t\t\t\t// Back edge found - cycle detected\n\t\t\t\tconst cyclePath = reconstructCycle(nodeId, neighborId)\n\t\t\t\tconst allowed = isCycleAllowed(cyclePath, screenMap)\n\t\t\t\tcycles.push({ cycle: cyclePath, allowed })\n\t\t\t} else if (neighborColor === Color.White) {\n\t\t\t\t// Only visit if the neighbor exists in our screen map\n\t\t\t\tif (screenMap.has(neighborId)) {\n\t\t\t\t\tdfs(neighborId, nodeId)\n\t\t\t\t}\n\t\t\t}\n\t\t\t// If Black, already fully processed - skip\n\t\t}\n\n\t\tcolor.set(nodeId, Color.Black)\n\t}\n\n\t/**\n\t * Reconstruct cycle path from back edge\n\t */\n\tfunction reconstructCycle(from: string, to: string): string[] {\n\t\tconst path: string[] = []\n\t\tlet current: string | null | undefined = from\n\t\tconst visited = new Set<string>()\n\t\tconst maxIterations = screenMap.size + 1\n\n\t\t// Trace back from 'from' to 'to' with loop guard\n\t\twhile (\n\t\t\tcurrent &&\n\t\t\tcurrent !== to &&\n\t\t\t!visited.has(current) &&\n\t\t\tpath.length < maxIterations\n\t\t) {\n\t\t\tvisited.add(current)\n\t\t\tpath.unshift(current)\n\t\t\tcurrent = parent.get(current)\n\t\t}\n\n\t\t// Add 'to' at the beginning (the cycle start)\n\t\tpath.unshift(to)\n\n\t\t// Add 'to' at the end to close the cycle\n\t\tpath.push(to)\n\n\t\treturn path\n\t}\n\n\tconst disallowedCycles = cycles.filter((c) => !c.allowed)\n\n\treturn {\n\t\thasCycles: cycles.length > 0,\n\t\tcycles,\n\t\tdisallowedCycles,\n\t\tduplicateIds,\n\t}\n}\n\n/**\n * Check if any screen in the cycle has allowCycles: true\n */\nfunction isCycleAllowed(\n\tcyclePath: string[],\n\tscreenMap: Map<string, Screen>,\n): boolean {\n\t// Exclude the last element as it's a duplicate of the first\n\tconst uniqueNodes = cyclePath.slice(0, -1)\n\n\tfor (const nodeId of uniqueNodes) {\n\t\tconst screen = screenMap.get(nodeId)\n\t\tif (screen?.allowCycles === true) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n/**\n * Format cycle information for console output\n *\n * @example\n * ```\n * Cycle 1: A → B → C → A\n * Cycle 2 (allowed): D → E → D\n * ```\n */\nexport function formatCycleWarnings(cycles: CycleInfo[]): string {\n\tif (cycles.length === 0) {\n\t\treturn \"\"\n\t}\n\n\tconst lines: string[] = []\n\n\tfor (let i = 0; i < cycles.length; i++) {\n\t\tconst cycle = cycles[i]\n\t\tif (!cycle) continue\n\n\t\tconst cycleStr = cycle.cycle.join(\" → \")\n\t\tconst allowedSuffix = cycle.allowed ? \" (allowed)\" : \"\"\n\t\tlines.push(` Cycle ${i + 1}${allowedSuffix}: ${cycleStr}`)\n\t}\n\n\treturn lines.join(\"\\n\")\n}\n\n/**\n * Get a summary of cycle detection results\n */\nexport function getCycleSummary(result: CycleDetectionResult): string {\n\tif (!result.hasCycles) {\n\t\treturn \"No circular navigation detected\"\n\t}\n\n\tconst total = result.cycles.length\n\tconst disallowed = result.disallowedCycles.length\n\tconst allowed = total - disallowed\n\n\tif (disallowed === 0) {\n\t\treturn `${total} circular navigation${total > 1 ? \"s\" : \"\"} detected (all allowed)`\n\t}\n\n\tif (allowed === 0) {\n\t\treturn `${total} circular navigation${total > 1 ? \"s\" : \"\"} detected`\n\t}\n\n\treturn `${total} circular navigation${total > 1 ? \"s\" : \"\"} detected (${disallowed} not allowed, ${allowed} allowed)`\n}\n","import type { ErrorOptions } from \"./logger.js\"\n\n/**\n * Centralized error definitions for consistent error messages across CLI commands\n */\nexport const ERRORS = {\n\t// ============================================\n\t// Configuration Errors\n\t// ============================================\n\n\tROUTES_PATTERN_MISSING: {\n\t\ttitle: \"routesPattern not configured\",\n\t\tsuggestion:\n\t\t\t\"Add routesPattern to your screenbook.config.ts to specify where your route files are located.\",\n\t\texample: `import { defineConfig } from \"@screenbook/core\"\n\nexport default defineConfig({\n routesPattern: \"src/pages/**/page.tsx\", // Adjust for your framework\n})`,\n\t} satisfies ErrorOptions,\n\n\tCONFIG_NOT_FOUND: {\n\t\ttitle: \"Configuration file not found\",\n\t\tsuggestion:\n\t\t\t\"Run 'screenbook init' to create a screenbook.config.ts file, or create one manually.\",\n\t\texample: `import { defineConfig } from \"@screenbook/core\"\n\nexport default defineConfig({\n metaPattern: \"src/**/screen.meta.ts\",\n})`,\n\t} satisfies ErrorOptions,\n\n\t// ============================================\n\t// Build/File Errors\n\t// ============================================\n\n\tSCREENS_NOT_FOUND: {\n\t\ttitle: \"screens.json not found\",\n\t\tsuggestion: \"Run 'screenbook build' first to generate the screen catalog.\",\n\t\tmessage:\n\t\t\t\"If you haven't set up Screenbook yet, run 'screenbook init' to get started.\",\n\t} satisfies ErrorOptions,\n\n\tSCREENS_PARSE_ERROR: {\n\t\ttitle: \"Failed to parse screens.json\",\n\t\tsuggestion:\n\t\t\t\"The screens.json file may be corrupted. Try running 'screenbook build' to regenerate it.\",\n\t} satisfies ErrorOptions,\n\n\tMETA_FILE_LOAD_ERROR: (filePath: string): ErrorOptions => ({\n\t\ttitle: `Failed to load ${filePath}`,\n\t\tsuggestion:\n\t\t\t\"Check the file for syntax errors or missing exports. The file should export a 'screen' object.\",\n\t\texample: `import { defineScreen } from \"@screenbook/core\"\n\nexport const screen = defineScreen({\n id: \"example.screen\",\n title: \"Example Screen\",\n route: \"/example\",\n})`,\n\t}),\n\n\t// ============================================\n\t// Command Argument Errors\n\t// ============================================\n\n\tAPI_NAME_REQUIRED: {\n\t\ttitle: \"API name is required\",\n\t\tsuggestion: \"Provide the API name as an argument.\",\n\t\texample: `screenbook impact UserAPI.getProfile\nscreenbook impact \"PaymentAPI.*\" # Use quotes for patterns`,\n\t} satisfies ErrorOptions,\n\n\t// ============================================\n\t// Git Errors\n\t// ============================================\n\n\tGIT_CHANGED_FILES_ERROR: (baseBranch: string): ErrorOptions => ({\n\t\ttitle: \"Failed to get changed files from git\",\n\t\tmessage: `Make sure you are in a git repository and the base branch '${baseBranch}' exists.`,\n\t\tsuggestion: `Verify the base branch exists with: git branch -a | grep ${baseBranch}`,\n\t}),\n\n\tGIT_NOT_REPOSITORY: {\n\t\ttitle: \"Not a git repository\",\n\t\tsuggestion:\n\t\t\t\"This command requires a git repository. Initialize one with 'git init' or navigate to an existing repository.\",\n\t} satisfies ErrorOptions,\n\n\t// ============================================\n\t// Server Errors\n\t// ============================================\n\n\tSERVER_START_FAILED: (error: string): ErrorOptions => ({\n\t\ttitle: \"Failed to start development server\",\n\t\tmessage: error,\n\t\tsuggestion:\n\t\t\t\"Check if the port is already in use or if there are any dependency issues.\",\n\t}),\n\n\t// ============================================\n\t// Validation Errors\n\t// ============================================\n\n\tVALIDATION_FAILED: (errorCount: number): ErrorOptions => ({\n\t\ttitle: `Validation failed with ${errorCount} error${errorCount === 1 ? \"\" : \"s\"}`,\n\t\tsuggestion:\n\t\t\t\"Fix the validation errors above. Screen references must point to existing screens.\",\n\t}),\n\n\t// ============================================\n\t// Lint Errors\n\t// ============================================\n\n\tLINT_MISSING_META: (\n\t\tmissingCount: number,\n\t\ttotalRoutes: number,\n\t): ErrorOptions => ({\n\t\ttitle: `${missingCount} route${missingCount === 1 ? \"\" : \"s\"} missing screen.meta.ts`,\n\t\tmessage: `Found ${totalRoutes} route file${totalRoutes === 1 ? \"\" : \"s\"}, but ${missingCount} ${missingCount === 1 ? \"is\" : \"are\"} missing colocated screen.meta.ts.`,\n\t\tsuggestion:\n\t\t\t\"Add screen.meta.ts files next to your route files, or run 'screenbook generate' to create them.\",\n\t}),\n\n\t// ============================================\n\t// Cycle Detection Errors\n\t// ============================================\n\n\tCYCLES_DETECTED: (cycleCount: number): ErrorOptions => ({\n\t\ttitle: `${cycleCount} circular navigation${cycleCount === 1 ? \"\" : \"s\"} detected`,\n\t\tsuggestion:\n\t\t\t\"Review the navigation flow. Use 'allowCycles: true' in screen.meta.ts to allow intentional cycles, or use --allow-cycles to suppress all warnings.\",\n\t\texample: `// Allow a specific screen to be part of cycles\nexport const screen = defineScreen({\n id: \"billing.invoice.detail\",\n next: [\"billing.invoices\"],\n allowCycles: true, // This cycle is intentional\n})`,\n\t}),\n}\n","import pc from \"picocolors\"\n\n/**\n * Structured error options for detailed error messages\n */\nexport interface ErrorOptions {\n\t/** Error title (shown after \"Error:\") */\n\ttitle: string\n\t/** Additional error message/details */\n\tmessage?: string\n\t/** Actionable suggestion for the user */\n\tsuggestion?: string\n\t/** Code example to help the user */\n\texample?: string\n}\n\n/**\n * Logger utility for consistent, color-coded CLI output\n */\nexport const logger = {\n\t// ============================================\n\t// Status Messages\n\t// ============================================\n\n\t/**\n\t * Success message (green checkmark)\n\t */\n\tsuccess: (msg: string): void => {\n\t\tconsole.log(`${pc.green(\"✓\")} ${msg}`)\n\t},\n\n\t/**\n\t * Error message (red X)\n\t */\n\terror: (msg: string): void => {\n\t\tconsole.error(`${pc.red(\"✗\")} ${pc.red(`Error: ${msg}`)}`)\n\t},\n\n\t/**\n\t * Warning message (yellow warning sign)\n\t */\n\twarn: (msg: string): void => {\n\t\tconsole.log(`${pc.yellow(\"⚠\")} ${pc.yellow(`Warning: ${msg}`)}`)\n\t},\n\n\t/**\n\t * Info message (cyan info icon)\n\t */\n\tinfo: (msg: string): void => {\n\t\tconsole.log(`${pc.cyan(\"ℹ\")} ${msg}`)\n\t},\n\n\t// ============================================\n\t// Detailed Error with Guidance\n\t// ============================================\n\n\t/**\n\t * Display a detailed error with actionable suggestions\n\t */\n\terrorWithHelp: (options: ErrorOptions): void => {\n\t\tconst { title, message, suggestion, example } = options\n\n\t\tconsole.error()\n\t\tconsole.error(`${pc.red(\"✗\")} ${pc.red(`Error: ${title}`)}`)\n\n\t\tif (message) {\n\t\t\tconsole.error()\n\t\t\tconsole.error(` ${message}`)\n\t\t}\n\n\t\tif (suggestion) {\n\t\t\tconsole.error()\n\t\t\tconsole.error(` ${pc.cyan(\"Suggestion:\")} ${suggestion}`)\n\t\t}\n\n\t\tif (example) {\n\t\t\tconsole.error()\n\t\t\tconsole.error(` ${pc.dim(\"Example:\")}`)\n\t\t\tfor (const line of example.split(\"\\n\")) {\n\t\t\t\tconsole.error(` ${pc.dim(line)}`)\n\t\t\t}\n\t\t}\n\n\t\tconsole.error()\n\t},\n\n\t// ============================================\n\t// Progress Indicators\n\t// ============================================\n\n\t/**\n\t * Step indicator (dimmed arrow)\n\t */\n\tstep: (msg: string): void => {\n\t\tconsole.log(`${pc.dim(\"→\")} ${msg}`)\n\t},\n\n\t/**\n\t * Done/completed indicator (green checkmark with green text)\n\t */\n\tdone: (msg: string): void => {\n\t\tconsole.log(`${pc.green(\"✓\")} ${pc.green(msg)}`)\n\t},\n\n\t/**\n\t * Item success (green checkmark, indented)\n\t */\n\titemSuccess: (msg: string): void => {\n\t\tconsole.log(` ${pc.green(\"✓\")} ${msg}`)\n\t},\n\n\t/**\n\t * Item failure (red X, indented)\n\t */\n\titemError: (msg: string): void => {\n\t\tconsole.log(` ${pc.red(\"✗\")} ${msg}`)\n\t},\n\n\t/**\n\t * Item warning (yellow warning, indented)\n\t */\n\titemWarn: (msg: string): void => {\n\t\tconsole.log(` ${pc.yellow(\"⚠\")} ${msg}`)\n\t},\n\n\t// ============================================\n\t// Plain Output\n\t// ============================================\n\n\t/**\n\t * Plain log (no prefix)\n\t */\n\tlog: (msg: string): void => {\n\t\tconsole.log(msg)\n\t},\n\n\t/**\n\t * Blank line\n\t */\n\tblank: (): void => {\n\t\tconsole.log()\n\t},\n\n\t// ============================================\n\t// Formatting Helpers\n\t// ============================================\n\n\t/**\n\t * Bold text\n\t */\n\tbold: (msg: string): string => pc.bold(msg),\n\n\t/**\n\t * Dimmed text\n\t */\n\tdim: (msg: string): string => pc.dim(msg),\n\n\t/**\n\t * Code/command text (cyan)\n\t */\n\tcode: (msg: string): string => pc.cyan(msg),\n\n\t/**\n\t * File path text (underlined)\n\t */\n\tpath: (msg: string): string => pc.underline(msg),\n\n\t/**\n\t * Highlight text (cyan bold)\n\t */\n\thighlight: (msg: string): string => pc.cyan(pc.bold(msg)),\n\n\t/**\n\t * Green text\n\t */\n\tgreen: (msg: string): string => pc.green(msg),\n\n\t/**\n\t * Red text\n\t */\n\tred: (msg: string): string => pc.red(msg),\n\n\t/**\n\t * Yellow text\n\t */\n\tyellow: (msg: string): string => pc.yellow(msg),\n}\n","import type { Screen } from \"@screenbook/core\"\n\nexport interface ValidationError {\n\tscreenId: string\n\tfield: \"next\" | \"entryPoints\"\n\tinvalidRef: string\n\tsuggestion?: string\n}\n\nexport interface ValidationResult {\n\tvalid: boolean\n\terrors: ValidationError[]\n}\n\n/**\n * Validate screen references (next and entryPoints)\n */\nexport function validateScreenReferences(screens: Screen[]): ValidationResult {\n\tconst screenIds = new Set(screens.map((s) => s.id))\n\tconst errors: ValidationError[] = []\n\n\tfor (const screen of screens) {\n\t\t// Validate next references\n\t\tif (screen.next) {\n\t\t\tfor (const nextId of screen.next) {\n\t\t\t\tif (!screenIds.has(nextId)) {\n\t\t\t\t\terrors.push({\n\t\t\t\t\t\tscreenId: screen.id,\n\t\t\t\t\t\tfield: \"next\",\n\t\t\t\t\t\tinvalidRef: nextId,\n\t\t\t\t\t\tsuggestion: findSimilar(nextId, screenIds),\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Validate entryPoints references\n\t\tif (screen.entryPoints) {\n\t\t\tfor (const entryId of screen.entryPoints) {\n\t\t\t\tif (!screenIds.has(entryId)) {\n\t\t\t\t\terrors.push({\n\t\t\t\t\t\tscreenId: screen.id,\n\t\t\t\t\t\tfield: \"entryPoints\",\n\t\t\t\t\t\tinvalidRef: entryId,\n\t\t\t\t\t\tsuggestion: findSimilar(entryId, screenIds),\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\tvalid: errors.length === 0,\n\t\terrors,\n\t}\n}\n\n/**\n * Find similar screen ID using Levenshtein distance\n */\nfunction findSimilar(\n\ttarget: string,\n\tcandidates: Set<string>,\n): string | undefined {\n\tlet bestMatch: string | undefined\n\tlet bestDistance = Number.POSITIVE_INFINITY\n\n\t// Only suggest if distance is reasonable (less than 40% of target length)\n\tconst maxDistance = Math.ceil(target.length * 0.4)\n\n\tfor (const candidate of candidates) {\n\t\tconst distance = levenshteinDistance(target, candidate)\n\t\tif (distance < bestDistance && distance <= maxDistance) {\n\t\t\tbestDistance = distance\n\t\t\tbestMatch = candidate\n\t\t}\n\t}\n\n\treturn bestMatch\n}\n\n/**\n * Calculate Levenshtein distance between two strings\n */\nfunction levenshteinDistance(a: string, b: string): number {\n\t// Pre-initialize matrix with proper dimensions\n\tconst matrix: number[][] = Array.from({ length: a.length + 1 }, () =>\n\t\tArray.from({ length: b.length + 1 }, () => 0),\n\t)\n\n\t// Helper to safely get/set matrix values (matrix is pre-initialized, so these are always valid)\n\tconst get = (i: number, j: number): number => matrix[i]?.[j] ?? 0\n\tconst set = (i: number, j: number, value: number): void => {\n\t\tconst row = matrix[i]\n\t\tif (row) row[j] = value\n\t}\n\n\t// Initialize first column\n\tfor (let i = 0; i <= a.length; i++) {\n\t\tset(i, 0, i)\n\t}\n\n\t// Initialize first row\n\tfor (let j = 0; j <= b.length; j++) {\n\t\tset(0, j, j)\n\t}\n\n\t// Fill the matrix\n\tfor (let i = 1; i <= a.length; i++) {\n\t\tfor (let j = 1; j <= b.length; j++) {\n\t\t\tconst cost = a[i - 1] === b[j - 1] ? 0 : 1\n\t\t\tset(\n\t\t\t\ti,\n\t\t\t\tj,\n\t\t\t\tMath.min(\n\t\t\t\t\tget(i - 1, j) + 1, // deletion\n\t\t\t\t\tget(i, j - 1) + 1, // insertion\n\t\t\t\t\tget(i - 1, j - 1) + cost, // substitution\n\t\t\t\t),\n\t\t\t)\n\t\t}\n\t}\n\n\treturn get(a.length, b.length)\n}\n\n/**\n * Format validation errors for console output\n */\nexport function formatValidationErrors(errors: ValidationError[]): string {\n\tconst lines: string[] = []\n\n\tfor (const error of errors) {\n\t\tlines.push(` Screen \"${error.screenId}\"`)\n\t\tlines.push(\n\t\t\t` → ${error.field} references non-existent screen \"${error.invalidRef}\"`,\n\t\t)\n\t\tif (error.suggestion) {\n\t\t\tlines.push(` Did you mean \"${error.suggestion}\"?`)\n\t\t}\n\t\tlines.push(\"\")\n\t}\n\n\treturn lines.join(\"\\n\")\n}\n","import { existsSync, mkdirSync, writeFileSync } from \"node:fs\"\nimport { dirname, join, resolve } from \"node:path\"\nimport type { Screen } from \"@screenbook/core\"\nimport { define } from \"gunshi\"\nimport { createJiti } from \"jiti\"\nimport { glob } from \"tinyglobby\"\nimport { loadConfig } from \"../utils/config.js\"\nimport {\n\tdetectCycles,\n\tformatCycleWarnings,\n\tgetCycleSummary,\n} from \"../utils/cycleDetection.js\"\nimport { ERRORS } from \"../utils/errors.js\"\nimport { logger } from \"../utils/logger.js\"\nimport {\n\tformatValidationErrors,\n\tvalidateScreenReferences,\n} from \"../utils/validation.js\"\n\nexport interface CoverageData {\n\ttotal: number\n\tcovered: number\n\tpercentage: number\n\tmissing: Array<{\n\t\troute: string\n\t\tsuggestedPath: string\n\t}>\n\tbyOwner: Record<string, { count: number; screens: string[] }>\n\tbyTag: Record<string, number>\n\ttimestamp: string\n}\n\nexport const buildCommand = define({\n\tname: \"build\",\n\tdescription: \"Build screen metadata JSON from screen.meta.ts files\",\n\targs: {\n\t\tconfig: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"c\",\n\t\t\tdescription: \"Path to config file\",\n\t\t},\n\t\toutDir: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"o\",\n\t\t\tdescription: \"Output directory\",\n\t\t},\n\t\tstrict: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"s\",\n\t\t\tdescription: \"Fail on validation errors and disallowed cycles\",\n\t\t\tdefault: false,\n\t\t},\n\t\tallowCycles: {\n\t\t\ttype: \"boolean\",\n\t\t\tdescription: \"Suppress all circular navigation warnings\",\n\t\t\tdefault: false,\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tconst config = await loadConfig(ctx.values.config)\n\t\tconst outDir = ctx.values.outDir ?? config.outDir\n\t\tconst cwd = process.cwd()\n\n\t\tlogger.info(\"Building screen metadata...\")\n\n\t\t// Find all screen.meta.ts files\n\t\tconst files = await glob(config.metaPattern, {\n\t\t\tcwd,\n\t\t\tignore: config.ignore,\n\t\t})\n\n\t\tif (files.length === 0) {\n\t\t\tlogger.warn(\n\t\t\t\t`No screen.meta.ts files found matching: ${config.metaPattern}`,\n\t\t\t)\n\t\t\treturn\n\t\t}\n\n\t\tlogger.info(`Found ${files.length} screen files`)\n\n\t\t// Create jiti instance for loading TypeScript files\n\t\tconst jiti = createJiti(cwd)\n\n\t\t// Extended screen type with file path (for internal use)\n\t\ttype ScreenWithFilePath = Screen & { filePath: string }\n\n\t\t// Load and collect screen metadata\n\t\tconst screens: ScreenWithFilePath[] = []\n\n\t\tfor (const file of files) {\n\t\t\tconst absolutePath = resolve(cwd, file)\n\n\t\t\ttry {\n\t\t\t\tconst module = (await jiti.import(absolutePath)) as { screen?: Screen }\n\t\t\t\tif (module.screen) {\n\t\t\t\t\tscreens.push({ ...module.screen, filePath: absolutePath })\n\t\t\t\t\tlogger.itemSuccess(module.screen.id)\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tlogger.itemError(`Failed to load ${file}`)\n\t\t\t\tif (error instanceof Error) {\n\t\t\t\t\tlogger.log(` ${logger.dim(error.message)}`)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Validate screen references\n\t\tconst validation = validateScreenReferences(screens)\n\t\tif (!validation.valid) {\n\t\t\tlogger.blank()\n\t\t\tlogger.warn(\"Invalid screen references found:\")\n\t\t\tlogger.log(formatValidationErrors(validation.errors))\n\n\t\t\tif (ctx.values.strict) {\n\t\t\t\tlogger.errorWithHelp(ERRORS.VALIDATION_FAILED(validation.errors.length))\n\t\t\t\tprocess.exit(1)\n\t\t\t}\n\t\t}\n\n\t\t// Detect circular navigation\n\t\tif (!ctx.values.allowCycles) {\n\t\t\tconst cycleResult = detectCycles(screens)\n\t\t\tif (cycleResult.hasCycles) {\n\t\t\t\tlogger.blank()\n\t\t\t\tlogger.warn(getCycleSummary(cycleResult))\n\t\t\t\tlogger.log(formatCycleWarnings(cycleResult.cycles))\n\n\t\t\t\tif (ctx.values.strict && cycleResult.disallowedCycles.length > 0) {\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tlogger.errorWithHelp(\n\t\t\t\t\t\tERRORS.CYCLES_DETECTED(cycleResult.disallowedCycles.length),\n\t\t\t\t\t)\n\t\t\t\t\tprocess.exit(1)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Generate screens.json\n\t\tconst outputPath = join(cwd, outDir, \"screens.json\")\n\t\tconst outputDir = dirname(outputPath)\n\n\t\tif (!existsSync(outputDir)) {\n\t\t\tmkdirSync(outputDir, { recursive: true })\n\t\t}\n\n\t\twriteFileSync(outputPath, JSON.stringify(screens, null, 2))\n\t\tlogger.blank()\n\t\tlogger.success(`Generated ${logger.path(outputPath)}`)\n\n\t\t// Generate Mermaid graph\n\t\tconst mermaidPath = join(cwd, outDir, \"graph.mmd\")\n\t\tconst mermaidContent = generateMermaidGraph(screens)\n\t\twriteFileSync(mermaidPath, mermaidContent)\n\t\tlogger.success(`Generated ${logger.path(mermaidPath)}`)\n\n\t\t// Generate coverage.json\n\t\tconst coverage = await generateCoverageData(config, cwd, screens)\n\t\tconst coveragePath = join(cwd, outDir, \"coverage.json\")\n\t\twriteFileSync(coveragePath, JSON.stringify(coverage, null, 2))\n\t\tlogger.success(`Generated ${logger.path(coveragePath)}`)\n\t\tlogger.blank()\n\t\tlogger.done(\n\t\t\t`Coverage: ${coverage.covered}/${coverage.total} (${coverage.percentage}%)`,\n\t\t)\n\t},\n})\n\nasync function generateCoverageData(\n\tconfig: { routesPattern?: string; metaPattern: string; ignore: string[] },\n\tcwd: string,\n\tscreens: Screen[],\n): Promise<CoverageData> {\n\t// Get all route files if routesPattern is configured\n\tlet routeFiles: string[] = []\n\tif (config.routesPattern) {\n\t\trouteFiles = await glob(config.routesPattern, {\n\t\t\tcwd,\n\t\t\tignore: config.ignore,\n\t\t})\n\t}\n\n\t// Get directories that have screen.meta.ts\n\tconst _metaDirs = new Set(\n\t\tscreens.map((s) => {\n\t\t\t// Extract directory from route or id\n\t\t\tconst parts = s.id.split(\".\")\n\t\t\treturn parts.slice(0, -1).join(\"/\") || parts[0]\n\t\t}),\n\t)\n\n\t// Find missing routes (routes without screen.meta.ts)\n\tconst missing: CoverageData[\"missing\"] = []\n\tfor (const routeFile of routeFiles) {\n\t\tconst routeDir = dirname(routeFile)\n\t\tconst hasMetaFile = screens.some((s) => {\n\t\t\t// Check if any screen's route matches this route file's directory\n\t\t\tconst screenDir = s.id.replace(/\\./g, \"/\")\n\t\t\treturn (\n\t\t\t\trouteDir.includes(screenDir) ||\n\t\t\t\tscreenDir.includes(\n\t\t\t\t\trouteDir.replace(/^src\\/pages\\//, \"\").replace(/^app\\//, \"\"),\n\t\t\t\t)\n\t\t\t)\n\t\t})\n\n\t\tif (!hasMetaFile) {\n\t\t\tmissing.push({\n\t\t\t\troute: routeFile,\n\t\t\t\tsuggestedPath: join(dirname(routeFile), \"screen.meta.ts\"),\n\t\t\t})\n\t\t}\n\t}\n\n\t// Calculate coverage\n\tconst total = routeFiles.length > 0 ? routeFiles.length : screens.length\n\tconst covered = screens.length\n\tconst percentage = total > 0 ? Math.round((covered / total) * 100) : 100\n\n\t// Group by owner\n\tconst byOwner: CoverageData[\"byOwner\"] = {}\n\tfor (const screen of screens) {\n\t\tconst owners = screen.owner || [\"unassigned\"]\n\t\tfor (const owner of owners) {\n\t\t\tif (!byOwner[owner]) {\n\t\t\t\tbyOwner[owner] = { count: 0, screens: [] }\n\t\t\t}\n\t\t\tbyOwner[owner].count++\n\t\t\tbyOwner[owner].screens.push(screen.id)\n\t\t}\n\t}\n\n\t// Group by tag\n\tconst byTag: CoverageData[\"byTag\"] = {}\n\tfor (const screen of screens) {\n\t\tconst tags = screen.tags || []\n\t\tfor (const tag of tags) {\n\t\t\tbyTag[tag] = (byTag[tag] || 0) + 1\n\t\t}\n\t}\n\n\treturn {\n\t\ttotal,\n\t\tcovered,\n\t\tpercentage,\n\t\tmissing,\n\t\tbyOwner,\n\t\tbyTag,\n\t\ttimestamp: new Date().toISOString(),\n\t}\n}\n\nfunction generateMermaidGraph(screens: Screen[]): string {\n\tconst lines: string[] = [\"flowchart TD\"]\n\n\t// Create nodes\n\tfor (const screen of screens) {\n\t\tconst label = screen.title.replace(/\"/g, \"'\")\n\t\tlines.push(` ${sanitizeId(screen.id)}[\"${label}\"]`)\n\t}\n\n\tlines.push(\"\")\n\n\t// Create edges from next relationships\n\tfor (const screen of screens) {\n\t\tif (screen.next) {\n\t\t\tfor (const nextId of screen.next) {\n\t\t\t\tlines.push(` ${sanitizeId(screen.id)} --> ${sanitizeId(nextId)}`)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn lines.join(\"\\n\")\n}\n\nfunction sanitizeId(id: string): string {\n\treturn id.replace(/\\./g, \"_\")\n}\n","import { spawn } from \"node:child_process\"\nimport { copyFileSync, existsSync, mkdirSync, writeFileSync } from \"node:fs\"\nimport { createRequire } from \"node:module\"\nimport { dirname, join, resolve } from \"node:path\"\nimport type { Screen } from \"@screenbook/core\"\nimport { define } from \"gunshi\"\nimport { createJiti } from \"jiti\"\nimport { glob } from \"tinyglobby\"\nimport { loadConfig } from \"../utils/config.js\"\nimport { ERRORS } from \"../utils/errors.js\"\nimport { logger } from \"../utils/logger.js\"\n\nexport const devCommand = define({\n\tname: \"dev\",\n\tdescription: \"Start the Screenbook development server\",\n\targs: {\n\t\tconfig: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"c\",\n\t\t\tdescription: \"Path to config file\",\n\t\t},\n\t\tport: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"p\",\n\t\t\tdescription: \"Port to run the server on\",\n\t\t\tdefault: \"4321\",\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tconst config = await loadConfig(ctx.values.config)\n\t\tconst port = ctx.values.port ?? \"4321\"\n\t\tconst cwd = process.cwd()\n\n\t\tlogger.info(\"Starting Screenbook development server...\")\n\n\t\t// First, build the screen metadata\n\t\tawait buildScreens(config, cwd)\n\n\t\t// Find the UI package location\n\t\tconst uiPackagePath = resolveUiPackage()\n\n\t\tif (!uiPackagePath) {\n\t\t\tlogger.errorWithHelp({\n\t\t\t\ttitle: \"Could not find @screenbook/ui package\",\n\t\t\t\tsuggestion:\n\t\t\t\t\t\"Make sure @screenbook/ui is installed. Run 'npm install @screenbook/ui' or 'pnpm add @screenbook/ui'.\",\n\t\t\t})\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\t// Copy screens.json and coverage.json to UI package\n\t\tconst screensJsonPath = join(cwd, config.outDir, \"screens.json\")\n\t\tconst coverageJsonPath = join(cwd, config.outDir, \"coverage.json\")\n\t\tconst uiScreensDir = join(uiPackagePath, \".screenbook\")\n\n\t\tif (!existsSync(uiScreensDir)) {\n\t\t\tmkdirSync(uiScreensDir, { recursive: true })\n\t\t}\n\n\t\tif (existsSync(screensJsonPath)) {\n\t\t\tcopyFileSync(screensJsonPath, join(uiScreensDir, \"screens.json\"))\n\t\t}\n\n\t\tif (existsSync(coverageJsonPath)) {\n\t\t\tcopyFileSync(coverageJsonPath, join(uiScreensDir, \"coverage.json\"))\n\t\t}\n\n\t\t// Start Astro dev server\n\t\tlogger.blank()\n\t\tlogger.info(\n\t\t\t`Starting UI server on ${logger.highlight(`http://localhost:${port}`)}`,\n\t\t)\n\n\t\tconst astroProcess = spawn(\"npx\", [\"astro\", \"dev\", \"--port\", port], {\n\t\t\tcwd: uiPackagePath,\n\t\t\tstdio: \"inherit\",\n\t\t\tshell: true,\n\t\t})\n\n\t\tastroProcess.on(\"error\", (error) => {\n\t\t\tlogger.errorWithHelp(ERRORS.SERVER_START_FAILED(error.message))\n\t\t\tprocess.exit(1)\n\t\t})\n\n\t\tastroProcess.on(\"close\", (code) => {\n\t\t\tprocess.exit(code ?? 0)\n\t\t})\n\n\t\t// Handle graceful shutdown\n\t\tprocess.on(\"SIGINT\", () => {\n\t\t\tastroProcess.kill(\"SIGINT\")\n\t\t})\n\n\t\tprocess.on(\"SIGTERM\", () => {\n\t\t\tastroProcess.kill(\"SIGTERM\")\n\t\t})\n\t},\n})\n\nasync function buildScreens(\n\tconfig: { metaPattern: string; outDir: string; ignore: string[] },\n\tcwd: string,\n): Promise<void> {\n\tconst files = await glob(config.metaPattern, {\n\t\tcwd,\n\t\tignore: config.ignore,\n\t})\n\n\tif (files.length === 0) {\n\t\tlogger.warn(`No screen.meta.ts files found matching: ${config.metaPattern}`)\n\t\treturn\n\t}\n\n\tlogger.info(`Found ${files.length} screen files`)\n\n\t// Extended screen type with file path (for internal use)\n\ttype ScreenWithFilePath = Screen & { filePath: string }\n\n\tconst jiti = createJiti(cwd)\n\tconst screens: ScreenWithFilePath[] = []\n\n\tfor (const file of files) {\n\t\tconst absolutePath = resolve(cwd, file)\n\n\t\ttry {\n\t\t\tconst module = (await jiti.import(absolutePath)) as { screen?: Screen }\n\t\t\tif (module.screen) {\n\t\t\t\tscreens.push({ ...module.screen, filePath: absolutePath })\n\t\t\t\tlogger.itemSuccess(module.screen.id)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tlogger.itemError(`Failed to load ${file}`)\n\t\t\tif (error instanceof Error) {\n\t\t\t\tlogger.log(` ${logger.dim(error.message)}`)\n\t\t\t}\n\t\t}\n\t}\n\n\tconst outputPath = join(cwd, config.outDir, \"screens.json\")\n\tconst outputDir = dirname(outputPath)\n\n\tif (!existsSync(outputDir)) {\n\t\tmkdirSync(outputDir, { recursive: true })\n\t}\n\n\twriteFileSync(outputPath, JSON.stringify(screens, null, 2))\n\tlogger.blank()\n\tlogger.success(`Generated ${logger.path(outputPath)}`)\n}\n\nfunction resolveUiPackage(): string | null {\n\t// Try to resolve @screenbook/ui from node_modules\n\ttry {\n\t\tconst require = createRequire(import.meta.url)\n\t\tconst uiPackageJson = require.resolve(\"@screenbook/ui/package.json\")\n\t\treturn dirname(uiPackageJson)\n\t} catch {\n\t\t// Fallback: look in common locations\n\t\tconst possiblePaths = [\n\t\t\tjoin(process.cwd(), \"node_modules\", \"@screenbook\", \"ui\"),\n\t\t\tjoin(process.cwd(), \"..\", \"ui\"),\n\t\t\tjoin(process.cwd(), \"packages\", \"ui\"),\n\t\t]\n\n\t\tfor (const p of possiblePaths) {\n\t\t\tif (existsSync(join(p, \"package.json\"))) {\n\t\t\t\treturn p\n\t\t\t}\n\t\t}\n\n\t\treturn null\n\t}\n}\n","import { existsSync, readFileSync } from \"node:fs\"\nimport { join, resolve } from \"node:path\"\nimport { define } from \"gunshi\"\nimport { glob } from \"tinyglobby\"\nimport { loadConfig } from \"../utils/config.js\"\nimport { logger } from \"../utils/logger.js\"\n\nconst CONFIG_FILES = [\n\t\"screenbook.config.ts\",\n\t\"screenbook.config.js\",\n\t\"screenbook.config.mjs\",\n]\n\nexport interface CheckResult {\n\tname: string\n\tstatus: \"pass\" | \"fail\" | \"warn\"\n\tmessage: string\n\tsuggestion?: string\n}\n\ninterface PackageJson {\n\tdependencies?: Record<string, string>\n\tdevDependencies?: Record<string, string>\n}\n\nexport const doctorCommand = define({\n\tname: \"doctor\",\n\tdescription: \"Diagnose common issues with Screenbook setup\",\n\targs: {\n\t\tverbose: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"v\",\n\t\t\tdescription: \"Show detailed output\",\n\t\t\tdefault: false,\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tconst cwd = process.cwd()\n\t\tconst verbose = ctx.values.verbose\n\n\t\tlogger.log(\"\")\n\t\tlogger.log(logger.bold(\"Screenbook Doctor\"))\n\t\tlogger.log(\"─────────────────\")\n\t\tlogger.log(\"\")\n\n\t\tconst results: CheckResult[] = []\n\n\t\t// Run all checks\n\t\tresults.push(await checkConfigFile(cwd))\n\t\tresults.push(await checkDependencies(cwd))\n\n\t\t// Load config for remaining checks\n\t\tconst config = await loadConfig()\n\n\t\tresults.push(await checkMetaPattern(cwd, config.metaPattern, config.ignore))\n\t\tresults.push(\n\t\t\tawait checkRoutesPattern(cwd, config.routesPattern, config.ignore),\n\t\t)\n\t\tresults.push(await checkBuildOutput(cwd, config.outDir))\n\t\tresults.push(await checkVersionCompatibility(cwd))\n\t\tresults.push(await checkGitRepository(cwd))\n\n\t\t// Display results\n\t\tdisplayResults(results, verbose)\n\t},\n})\n\n// Exported for testing\nexport async function checkConfigFile(cwd: string): Promise<CheckResult> {\n\tfor (const configFile of CONFIG_FILES) {\n\t\tconst absolutePath = resolve(cwd, configFile)\n\t\tif (existsSync(absolutePath)) {\n\t\t\treturn {\n\t\t\t\tname: \"Config file\",\n\t\t\t\tstatus: \"pass\",\n\t\t\t\tmessage: `Found: ${configFile}`,\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\tname: \"Config file\",\n\t\tstatus: \"warn\",\n\t\tmessage: \"No config file found (using defaults)\",\n\t\tsuggestion: \"Run 'screenbook init' to create a config file\",\n\t}\n}\n\nexport async function checkDependencies(cwd: string): Promise<CheckResult> {\n\tconst packageJsonPath = join(cwd, \"package.json\")\n\n\tif (!existsSync(packageJsonPath)) {\n\t\treturn {\n\t\t\tname: \"Dependencies\",\n\t\t\tstatus: \"fail\",\n\t\t\tmessage: \"package.json not found\",\n\t\t\tsuggestion: \"Run 'npm init' or 'pnpm init' to create package.json\",\n\t\t}\n\t}\n\n\ttry {\n\t\tconst content = readFileSync(packageJsonPath, \"utf-8\")\n\t\tconst pkg = JSON.parse(content) as PackageJson\n\n\t\tconst allDeps = { ...pkg.dependencies, ...pkg.devDependencies }\n\t\tconst unifiedVersion = allDeps.screenbook\n\t\tconst coreVersion = allDeps[\"@screenbook/core\"]\n\t\tconst cliVersion = allDeps[\"@screenbook/cli\"]\n\n\t\t// Check for unified screenbook package first\n\t\tif (unifiedVersion) {\n\t\t\treturn {\n\t\t\t\tname: \"Dependencies\",\n\t\t\t\tstatus: \"pass\",\n\t\t\t\tmessage: `screenbook@${unifiedVersion}`,\n\t\t\t}\n\t\t}\n\n\t\tif (!coreVersion && !cliVersion) {\n\t\t\treturn {\n\t\t\t\tname: \"Dependencies\",\n\t\t\t\tstatus: \"fail\",\n\t\t\t\tmessage: \"Screenbook packages not installed\",\n\t\t\t\tsuggestion:\n\t\t\t\t\t\"Run 'pnpm add -D screenbook' or 'pnpm add -D @screenbook/core @screenbook/cli' to install\",\n\t\t\t}\n\t\t}\n\n\t\tif (!coreVersion) {\n\t\t\treturn {\n\t\t\t\tname: \"Dependencies\",\n\t\t\t\tstatus: \"warn\",\n\t\t\t\tmessage: \"@screenbook/core not found in dependencies\",\n\t\t\t\tsuggestion: \"Run 'pnpm add -D @screenbook/core' to install\",\n\t\t\t}\n\t\t}\n\n\t\tif (!cliVersion) {\n\t\t\treturn {\n\t\t\t\tname: \"Dependencies\",\n\t\t\t\tstatus: \"warn\",\n\t\t\t\tmessage: \"@screenbook/cli not found in dependencies\",\n\t\t\t\tsuggestion: \"Run 'pnpm add -D @screenbook/cli' to install\",\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tname: \"Dependencies\",\n\t\t\tstatus: \"pass\",\n\t\t\tmessage: `@screenbook/core@${coreVersion}, @screenbook/cli@${cliVersion}`,\n\t\t}\n\t} catch {\n\t\treturn {\n\t\t\tname: \"Dependencies\",\n\t\t\tstatus: \"fail\",\n\t\t\tmessage: \"Failed to read package.json\",\n\t\t\tsuggestion: \"Ensure package.json is valid JSON\",\n\t\t}\n\t}\n}\n\nexport async function checkMetaPattern(\n\tcwd: string,\n\tmetaPattern: string,\n\tignore: string[],\n): Promise<CheckResult> {\n\ttry {\n\t\tconst files = await glob(metaPattern, { cwd, ignore })\n\n\t\tif (files.length === 0) {\n\t\t\treturn {\n\t\t\t\tname: \"Screen meta files\",\n\t\t\t\tstatus: \"warn\",\n\t\t\t\tmessage: `No files matching: ${metaPattern}`,\n\t\t\t\tsuggestion: \"Run 'screenbook generate' to create screen.meta.ts files\",\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tname: \"Screen meta files\",\n\t\t\tstatus: \"pass\",\n\t\t\tmessage: `Found ${files.length} screen.meta.ts file${files.length > 1 ? \"s\" : \"\"}`,\n\t\t}\n\t} catch {\n\t\treturn {\n\t\t\tname: \"Screen meta files\",\n\t\t\tstatus: \"fail\",\n\t\t\tmessage: `Invalid pattern: ${metaPattern}`,\n\t\t\tsuggestion: \"Check metaPattern in your config file\",\n\t\t}\n\t}\n}\n\nexport async function checkRoutesPattern(\n\tcwd: string,\n\troutesPattern: string | undefined,\n\tignore: string[],\n): Promise<CheckResult> {\n\tif (!routesPattern) {\n\t\treturn {\n\t\t\tname: \"Routes pattern\",\n\t\t\tstatus: \"warn\",\n\t\t\tmessage: \"routesPattern not configured\",\n\t\t\tsuggestion:\n\t\t\t\t\"Set routesPattern in config to enable 'lint' and 'generate' commands\",\n\t\t}\n\t}\n\n\ttry {\n\t\tconst files = await glob(routesPattern, { cwd, ignore })\n\n\t\tif (files.length === 0) {\n\t\t\treturn {\n\t\t\t\tname: \"Routes pattern\",\n\t\t\t\tstatus: \"warn\",\n\t\t\t\tmessage: `No files matching: ${routesPattern}`,\n\t\t\t\tsuggestion: \"Check routesPattern in your config file\",\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tname: \"Routes pattern\",\n\t\t\tstatus: \"pass\",\n\t\t\tmessage: `Found ${files.length} route file${files.length > 1 ? \"s\" : \"\"}`,\n\t\t}\n\t} catch {\n\t\treturn {\n\t\t\tname: \"Routes pattern\",\n\t\t\tstatus: \"fail\",\n\t\t\tmessage: `Invalid pattern: ${routesPattern}`,\n\t\t\tsuggestion: \"Check routesPattern in your config file\",\n\t\t}\n\t}\n}\n\nexport async function checkBuildOutput(\n\tcwd: string,\n\toutDir: string,\n): Promise<CheckResult> {\n\tconst screensJsonPath = join(cwd, outDir, \"screens.json\")\n\n\tif (!existsSync(screensJsonPath)) {\n\t\treturn {\n\t\t\tname: \"Build output\",\n\t\t\tstatus: \"fail\",\n\t\t\tmessage: `screens.json not found in ${outDir}/`,\n\t\t\tsuggestion: \"Run 'screenbook build' to generate metadata\",\n\t\t}\n\t}\n\n\ttry {\n\t\tconst content = readFileSync(screensJsonPath, \"utf-8\")\n\t\tconst screens = JSON.parse(content) as unknown[]\n\n\t\treturn {\n\t\t\tname: \"Build output\",\n\t\t\tstatus: \"pass\",\n\t\t\tmessage: `screens.json contains ${screens.length} screen${screens.length > 1 ? \"s\" : \"\"}`,\n\t\t}\n\t} catch {\n\t\treturn {\n\t\t\tname: \"Build output\",\n\t\t\tstatus: \"fail\",\n\t\t\tmessage: \"screens.json is corrupted\",\n\t\t\tsuggestion: \"Run 'screenbook build' to regenerate\",\n\t\t}\n\t}\n}\n\nexport async function checkVersionCompatibility(\n\tcwd: string,\n): Promise<CheckResult> {\n\tconst packageJsonPath = join(cwd, \"package.json\")\n\n\tif (!existsSync(packageJsonPath)) {\n\t\treturn {\n\t\t\tname: \"Version compatibility\",\n\t\t\tstatus: \"warn\",\n\t\t\tmessage: \"Cannot check - package.json not found\",\n\t\t}\n\t}\n\n\ttry {\n\t\tconst content = readFileSync(packageJsonPath, \"utf-8\")\n\t\tconst pkg = JSON.parse(content) as PackageJson\n\n\t\tconst allDeps = { ...pkg.dependencies, ...pkg.devDependencies }\n\t\tconst unifiedVersion = allDeps.screenbook\n\t\tconst coreVersion = allDeps[\"@screenbook/core\"]\n\t\tconst cliVersion = allDeps[\"@screenbook/cli\"]\n\n\t\t// Unified package - no compatibility check needed\n\t\tif (unifiedVersion) {\n\t\t\treturn {\n\t\t\t\tname: \"Version compatibility\",\n\t\t\t\tstatus: \"pass\",\n\t\t\t\tmessage: \"Using unified screenbook package\",\n\t\t\t}\n\t\t}\n\n\t\tif (!coreVersion || !cliVersion) {\n\t\t\treturn {\n\t\t\t\tname: \"Version compatibility\",\n\t\t\t\tstatus: \"warn\",\n\t\t\t\tmessage: \"Cannot check - packages not installed\",\n\t\t\t}\n\t\t}\n\n\t\t// Extract major version (handle ^, ~, etc.)\n\t\tconst extractMajor = (version: string): string => {\n\t\t\tconst cleaned = version.replace(/^[\\^~>=<]+/, \"\")\n\t\t\treturn cleaned.split(\".\")[0] ?? \"0\"\n\t\t}\n\n\t\tconst coreMajor = extractMajor(coreVersion)\n\t\tconst cliMajor = extractMajor(cliVersion)\n\n\t\tif (coreMajor !== cliMajor) {\n\t\t\treturn {\n\t\t\t\tname: \"Version compatibility\",\n\t\t\t\tstatus: \"warn\",\n\t\t\t\tmessage: `Major version mismatch: core@${coreVersion} vs cli@${cliVersion}`,\n\t\t\t\tsuggestion: \"Update packages to matching major versions\",\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tname: \"Version compatibility\",\n\t\t\tstatus: \"pass\",\n\t\t\tmessage: \"Package versions are compatible\",\n\t\t}\n\t} catch {\n\t\treturn {\n\t\t\tname: \"Version compatibility\",\n\t\t\tstatus: \"fail\",\n\t\t\tmessage: \"Failed to read package.json\",\n\t\t}\n\t}\n}\n\nexport async function checkGitRepository(cwd: string): Promise<CheckResult> {\n\tconst gitDir = join(cwd, \".git\")\n\n\tif (!existsSync(gitDir)) {\n\t\treturn {\n\t\t\tname: \"Git repository\",\n\t\t\tstatus: \"warn\",\n\t\t\tmessage: \"Not a git repository\",\n\t\t\tsuggestion:\n\t\t\t\t\"Run 'git init' to enable 'pr-impact' command for PR analysis\",\n\t\t}\n\t}\n\n\treturn {\n\t\tname: \"Git repository\",\n\t\tstatus: \"pass\",\n\t\tmessage: \"Git repository detected\",\n\t}\n}\n\nfunction displayResults(results: CheckResult[], verbose: boolean): void {\n\tlet passCount = 0\n\tlet failCount = 0\n\tlet warnCount = 0\n\n\tfor (const result of results) {\n\t\tconst icon =\n\t\t\tresult.status === \"pass\"\n\t\t\t\t? logger.green(\"✓\")\n\t\t\t\t: result.status === \"fail\"\n\t\t\t\t\t? logger.red(\"✗\")\n\t\t\t\t\t: logger.yellow(\"⚠\")\n\n\t\tconst statusColor =\n\t\t\tresult.status === \"pass\"\n\t\t\t\t? logger.green\n\t\t\t\t: result.status === \"fail\"\n\t\t\t\t\t? logger.red\n\t\t\t\t\t: logger.yellow\n\n\t\tlogger.log(`${icon} ${statusColor(result.name)}: ${result.message}`)\n\n\t\tif (result.suggestion && (result.status !== \"pass\" || verbose)) {\n\t\t\tlogger.log(` ${logger.dim(\"→\")} ${result.suggestion}`)\n\t\t}\n\n\t\tif (result.status === \"pass\") passCount++\n\t\telse if (result.status === \"fail\") failCount++\n\t\telse warnCount++\n\t}\n\n\tlogger.log(\"\")\n\n\tconst summary: string[] = []\n\tif (passCount > 0) summary.push(logger.green(`${passCount} passed`))\n\tif (failCount > 0) summary.push(logger.red(`${failCount} failed`))\n\tif (warnCount > 0) summary.push(logger.yellow(`${warnCount} warnings`))\n\n\tlogger.log(`Summary: ${summary.join(\", \")}`)\n\n\tif (failCount > 0) {\n\t\tlogger.log(\"\")\n\t\tlogger.log(\n\t\t\tlogger.dim(\"Run the suggested commands above to fix the issues.\"),\n\t\t)\n\t}\n}\n","import { existsSync, writeFileSync } from \"node:fs\"\nimport { dirname, join, relative } from \"node:path\"\nimport { define } from \"gunshi\"\nimport prompts from \"prompts\"\nimport { glob } from \"tinyglobby\"\nimport { loadConfig } from \"../utils/config.js\"\nimport { ERRORS } from \"../utils/errors.js\"\nimport { logger } from \"../utils/logger.js\"\n\nexport const generateCommand = define({\n\tname: \"generate\",\n\tdescription: \"Auto-generate screen.meta.ts files from route files\",\n\targs: {\n\t\tconfig: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"c\",\n\t\t\tdescription: \"Path to config file\",\n\t\t},\n\t\tdryRun: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"n\",\n\t\t\tdescription: \"Show what would be generated without writing files\",\n\t\t\tdefault: false,\n\t\t},\n\t\tforce: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"f\",\n\t\t\tdescription: \"Overwrite existing screen.meta.ts files\",\n\t\t\tdefault: false,\n\t\t},\n\t\tinteractive: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"i\",\n\t\t\tdescription: \"Interactively confirm or modify each screen\",\n\t\t\tdefault: false,\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tconst config = await loadConfig(ctx.values.config)\n\t\tconst cwd = process.cwd()\n\t\tconst dryRun = ctx.values.dryRun ?? false\n\t\tconst force = ctx.values.force ?? false\n\t\tconst interactive = ctx.values.interactive ?? false\n\n\t\tif (!config.routesPattern) {\n\t\t\tlogger.errorWithHelp(ERRORS.ROUTES_PATTERN_MISSING)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tlogger.info(\"Scanning for route files...\")\n\t\tlogger.blank()\n\n\t\t// Find all route files\n\t\tconst routeFiles = await glob(config.routesPattern, {\n\t\t\tcwd,\n\t\t\tignore: config.ignore,\n\t\t})\n\n\t\tif (routeFiles.length === 0) {\n\t\t\tlogger.warn(`No route files found matching: ${config.routesPattern}`)\n\t\t\treturn\n\t\t}\n\n\t\tlogger.log(`Found ${routeFiles.length} route files`)\n\t\tlogger.blank()\n\n\t\tlet created = 0\n\t\tlet skipped = 0\n\n\t\tfor (const routeFile of routeFiles) {\n\t\t\tconst routeDir = dirname(routeFile)\n\t\t\tconst metaPath = join(routeDir, \"screen.meta.ts\")\n\t\t\tconst absoluteMetaPath = join(cwd, metaPath)\n\n\t\t\tif (!force && existsSync(absoluteMetaPath)) {\n\t\t\t\tif (!interactive) {\n\t\t\t\t\tskipped++\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tlogger.itemWarn(\n\t\t\t\t\t`Exists: ${logger.path(metaPath)} (use --force to overwrite)`,\n\t\t\t\t)\n\t\t\t\tskipped++\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Generate screen metadata from path\n\t\t\tconst screenMeta = inferScreenMeta(routeDir, config.routesPattern)\n\n\t\t\tif (interactive) {\n\t\t\t\tconst result = await promptForScreen(routeFile, screenMeta)\n\n\t\t\t\tif (result.skip) {\n\t\t\t\t\tlogger.itemWarn(`Skipped: ${logger.path(metaPath)}`)\n\t\t\t\t\tskipped++\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tconst content = generateScreenMetaContent(result.meta, {\n\t\t\t\t\towner: result.owner,\n\t\t\t\t\ttags: result.tags,\n\t\t\t\t})\n\n\t\t\t\tif (dryRun) {\n\t\t\t\t\tlogger.step(`Would create: ${logger.path(metaPath)}`)\n\t\t\t\t\tlogger.log(` ${logger.dim(`id: \"${result.meta.id}\"`)}`)\n\t\t\t\t\tlogger.log(` ${logger.dim(`title: \"${result.meta.title}\"`)}`)\n\t\t\t\t\tlogger.log(` ${logger.dim(`route: \"${result.meta.route}\"`)}`)\n\t\t\t\t\tif (result.owner.length > 0) {\n\t\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t\t` ${logger.dim(`owner: [${result.owner.map((o) => `\"${o}\"`).join(\", \")}]`)}`,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\tif (result.tags.length > 0) {\n\t\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t\t` ${logger.dim(`tags: [${result.tags.map((t) => `\"${t}\"`).join(\", \")}]`)}`,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\tlogger.blank()\n\t\t\t\t} else {\n\t\t\t\t\twriteFileSync(absoluteMetaPath, content)\n\t\t\t\t\tlogger.itemSuccess(`Created: ${logger.path(metaPath)}`)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst content = generateScreenMetaContent(screenMeta)\n\n\t\t\t\tif (dryRun) {\n\t\t\t\t\tlogger.step(`Would create: ${logger.path(metaPath)}`)\n\t\t\t\t\tlogger.log(` ${logger.dim(`id: \"${screenMeta.id}\"`)}`)\n\t\t\t\t\tlogger.log(` ${logger.dim(`title: \"${screenMeta.title}\"`)}`)\n\t\t\t\t\tlogger.log(` ${logger.dim(`route: \"${screenMeta.route}\"`)}`)\n\t\t\t\t\tlogger.blank()\n\t\t\t\t} else {\n\t\t\t\t\twriteFileSync(absoluteMetaPath, content)\n\t\t\t\t\tlogger.itemSuccess(`Created: ${logger.path(metaPath)}`)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcreated++\n\t\t}\n\n\t\tlogger.blank()\n\t\tif (dryRun) {\n\t\t\tlogger.info(`Would create ${created} files (${skipped} already exist)`)\n\t\t\tlogger.blank()\n\t\t\tlogger.log(`Run without ${logger.code(\"--dry-run\")} to create files`)\n\t\t} else {\n\t\t\tlogger.done(`Created ${created} files (${skipped} skipped)`)\n\t\t\tif (created > 0) {\n\t\t\t\tlogger.blank()\n\t\t\t\tlogger.log(logger.bold(\"Next steps:\"))\n\t\t\t\tlogger.log(\n\t\t\t\t\t\" 1. Review and customize the generated screen.meta.ts files\",\n\t\t\t\t)\n\t\t\t\tlogger.log(\n\t\t\t\t\t` 2. Run ${logger.code(\"screenbook dev\")} to view your screen catalog`,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t},\n})\n\ninterface InferredScreenMeta {\n\tid: string\n\ttitle: string\n\troute: string\n}\n\ninterface InteractiveResult {\n\tskip: boolean\n\tmeta: InferredScreenMeta\n\towner: string[]\n\ttags: string[]\n}\n\n/**\n * Parse comma-separated string into array\n */\nexport function parseCommaSeparated(input: string): string[] {\n\tif (!input.trim()) return []\n\treturn input\n\t\t.split(\",\")\n\t\t.map((s) => s.trim())\n\t\t.filter(Boolean)\n}\n\n/**\n * Prompt user for screen metadata in interactive mode\n */\nasync function promptForScreen(\n\trouteFile: string,\n\tinferred: InferredScreenMeta,\n): Promise<InteractiveResult> {\n\tlogger.blank()\n\tlogger.info(`Found: ${logger.path(routeFile)}`)\n\tlogger.blank()\n\tlogger.log(\n\t\t` ${logger.dim(\"ID:\")} ${inferred.id} ${logger.dim(\"(inferred)\")}`,\n\t)\n\tlogger.log(\n\t\t` ${logger.dim(\"Title:\")} ${inferred.title} ${logger.dim(\"(inferred)\")}`,\n\t)\n\tlogger.log(\n\t\t` ${logger.dim(\"Route:\")} ${inferred.route} ${logger.dim(\"(inferred)\")}`,\n\t)\n\tlogger.blank()\n\n\tconst response = await prompts([\n\t\t{\n\t\t\ttype: \"confirm\",\n\t\t\tname: \"proceed\",\n\t\t\tmessage: \"Generate this screen?\",\n\t\t\tinitial: true,\n\t\t},\n\t\t{\n\t\t\ttype: (prev) => (prev ? \"text\" : null),\n\t\t\tname: \"id\",\n\t\t\tmessage: \"ID\",\n\t\t\tinitial: inferred.id,\n\t\t},\n\t\t{\n\t\t\ttype: (_prev, values) => (values.proceed ? \"text\" : null),\n\t\t\tname: \"title\",\n\t\t\tmessage: \"Title\",\n\t\t\tinitial: inferred.title,\n\t\t},\n\t\t{\n\t\t\ttype: (_prev, values) => (values.proceed ? \"text\" : null),\n\t\t\tname: \"owner\",\n\t\t\tmessage: \"Owner (comma-separated)\",\n\t\t\tinitial: \"\",\n\t\t},\n\t\t{\n\t\t\ttype: (_prev, values) => (values.proceed ? \"text\" : null),\n\t\t\tname: \"tags\",\n\t\t\tmessage: \"Tags (comma-separated)\",\n\t\t\tinitial: inferred.id.split(\".\")[0] || \"\",\n\t\t},\n\t])\n\n\tif (!response.proceed) {\n\t\treturn { skip: true, meta: inferred, owner: [], tags: [] }\n\t}\n\n\treturn {\n\t\tskip: false,\n\t\tmeta: {\n\t\t\tid: response.id || inferred.id,\n\t\t\ttitle: response.title || inferred.title,\n\t\t\troute: inferred.route,\n\t\t},\n\t\towner: parseCommaSeparated(response.owner || \"\"),\n\t\ttags: parseCommaSeparated(response.tags || \"\"),\n\t}\n}\n\n/**\n * Infer screen metadata from the route file path\n */\nfunction inferScreenMeta(\n\trouteDir: string,\n\troutesPattern: string,\n): InferredScreenMeta {\n\t// Extract base directory from pattern (e.g., \"src/pages\" from \"src/pages/**/page.tsx\")\n\tconst patternBase = routesPattern.split(\"*\")[0]?.replace(/\\/$/, \"\") ?? \"\"\n\n\t// Get relative path from pattern base\n\tconst relativePath = relative(patternBase, routeDir)\n\n\t// Handle root route\n\tif (!relativePath || relativePath === \".\") {\n\t\treturn {\n\t\t\tid: \"home\",\n\t\t\ttitle: \"Home\",\n\t\t\troute: \"/\",\n\t\t}\n\t}\n\n\t// Clean up path segments (remove route groups like (marketing), handle dynamic segments)\n\tconst segments = relativePath\n\t\t.split(\"/\")\n\t\t.filter((s) => s && !s.startsWith(\"(\") && !s.endsWith(\")\"))\n\t\t.map((s) =>\n\t\t\ts.replace(/^\\[\\.\\.\\..*\\]$/, \"catchall\").replace(/^\\[(.+)\\]$/, \"$1\"),\n\t\t)\n\n\t// Generate ID from segments (e.g., \"billing.invoice.detail\")\n\tconst id = segments.join(\".\")\n\n\t// Generate title from last segment (e.g., \"Invoice Detail\" from \"invoice-detail\")\n\tconst lastSegment = segments[segments.length - 1] || \"home\"\n\tconst title = lastSegment\n\t\t.split(/[-_]/)\n\t\t.map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n\t\t.join(\" \")\n\n\t// Generate route from path (e.g., \"/billing/invoice/:id\")\n\tconst routeSegments = relativePath\n\t\t.split(\"/\")\n\t\t.filter((s) => s && !s.startsWith(\"(\") && !s.endsWith(\")\"))\n\t\t.map((s) => {\n\t\t\t// Convert [id] to :id, [...slug] to *\n\t\t\tif (s.startsWith(\"[...\") && s.endsWith(\"]\")) {\n\t\t\t\treturn \"*\"\n\t\t\t}\n\t\t\tif (s.startsWith(\"[\") && s.endsWith(\"]\")) {\n\t\t\t\treturn `:${s.slice(1, -1)}`\n\t\t\t}\n\t\t\treturn s\n\t\t})\n\tconst route = `/${routeSegments.join(\"/\")}`\n\n\treturn { id, title, route }\n}\n\ninterface GenerateOptions {\n\towner?: string[]\n\ttags?: string[]\n}\n\n/**\n * Generate screen.meta.ts file content\n */\nfunction generateScreenMetaContent(\n\tmeta: InferredScreenMeta,\n\toptions?: GenerateOptions,\n): string {\n\t// Use provided values or infer defaults\n\tconst owner = options?.owner ?? []\n\tconst tags =\n\t\toptions?.tags && options.tags.length > 0\n\t\t\t? options.tags\n\t\t\t: [meta.id.split(\".\")[0] || \"general\"]\n\n\tconst ownerStr =\n\t\towner.length > 0 ? `[${owner.map((o) => `\"${o}\"`).join(\", \")}]` : \"[]\"\n\tconst tagsStr = `[${tags.map((t) => `\"${t}\"`).join(\", \")}]`\n\n\treturn `import { defineScreen } from \"@screenbook/core\"\n\nexport const screen = defineScreen({\n\tid: \"${meta.id}\",\n\ttitle: \"${meta.title}\",\n\troute: \"${meta.route}\",\n\n\t// Team or individual responsible for this screen\n\towner: ${ownerStr},\n\n\t// Tags for filtering in the catalog\n\ttags: ${tagsStr},\n\n\t// APIs/services this screen depends on (for impact analysis)\n\t// Example: [\"UserAPI.getProfile\", \"PaymentService.checkout\"]\n\tdependsOn: [],\n\n\t// Screen IDs that can navigate to this screen\n\tentryPoints: [],\n\n\t// Screen IDs this screen can navigate to\n\tnext: [],\n})\n`\n}\n","import type { Screen } from \"@screenbook/core\"\n\nexport interface TransitiveDependency {\n\tscreen: Screen\n\tpath: string[]\n}\n\nexport interface ImpactResult {\n\tapi: string\n\tdirect: Screen[]\n\ttransitive: TransitiveDependency[]\n\ttotalCount: number\n}\n\n/**\n * Check if a screen's dependsOn matches the API name (supports partial matching).\n * - \"InvoiceAPI\" matches \"InvoiceAPI.getDetail\"\n * - \"InvoiceAPI.getDetail\" matches \"InvoiceAPI.getDetail\"\n */\nfunction matchesDependency(dependency: string, apiName: string): boolean {\n\t// Exact match\n\tif (dependency === apiName) {\n\t\treturn true\n\t}\n\t// Partial match: apiName is a prefix of dependency\n\tif (dependency.startsWith(`${apiName}.`)) {\n\t\treturn true\n\t}\n\t// Partial match: dependency is a prefix of apiName\n\tif (apiName.startsWith(`${dependency}.`)) {\n\t\treturn true\n\t}\n\treturn false\n}\n\n/**\n * Find screens that directly depend on the given API.\n */\nfunction findDirectDependents(screens: Screen[], apiName: string): Screen[] {\n\treturn screens.filter((screen) =>\n\t\tscreen.dependsOn?.some((dep) => matchesDependency(dep, apiName)),\n\t)\n}\n\n/**\n * Build a reverse navigation graph: screenId -> screens that can navigate to it.\n * This is built from the `entryPoints` field.\n */\nfunction _buildReverseNavigationGraph(\n\tscreens: Screen[],\n): Map<string, Set<string>> {\n\tconst graph = new Map<string, Set<string>>()\n\n\tfor (const screen of screens) {\n\t\t// entryPoints lists screens that can navigate TO this screen\n\t\t// So we want to find screens that navigate FROM here\n\t\t// Actually, we need to reverse: if A.entryPoints includes B,\n\t\t// then B can navigate to A, so B is affected if A is affected\n\n\t\t// For transitive analysis:\n\t\t// If screen A depends on API, and screen B has entryPoints: [\"A\"],\n\t\t// then B can navigate to A, meaning users can reach A from B\n\t\t// So if A is impacted, we should show B as transitively impacted\n\n\t\tif (!screen.entryPoints) continue\n\n\t\tfor (const entryPoint of screen.entryPoints) {\n\t\t\tif (!graph.has(screen.id)) {\n\t\t\t\tgraph.set(screen.id, new Set())\n\t\t\t}\n\t\t\tgraph.get(screen.id)?.add(entryPoint)\n\t\t}\n\t}\n\n\treturn graph\n}\n\n/**\n * Build a navigation graph from `next` field: screenId -> screens it can navigate to.\n */\nfunction buildNavigationGraph(screens: Screen[]): Map<string, Set<string>> {\n\tconst graph = new Map<string, Set<string>>()\n\n\tfor (const screen of screens) {\n\t\tif (!screen.next) continue\n\n\t\tif (!graph.has(screen.id)) {\n\t\t\tgraph.set(screen.id, new Set())\n\t\t}\n\t\tfor (const nextId of screen.next) {\n\t\t\tgraph.get(screen.id)?.add(nextId)\n\t\t}\n\t}\n\n\treturn graph\n}\n\n/**\n * Find all transitive dependents using BFS.\n * A screen is transitively dependent if it can navigate to a directly dependent screen.\n */\nfunction findTransitiveDependents(\n\tscreens: Screen[],\n\tdirectDependentIds: Set<string>,\n\tmaxDepth: number,\n): TransitiveDependency[] {\n\tconst _screenMap = new Map(screens.map((s) => [s.id, s]))\n\tconst navigationGraph = buildNavigationGraph(screens)\n\tconst transitive: TransitiveDependency[] = []\n\tconst visited = new Set<string>()\n\n\t// For each screen, check if it can reach a directly dependent screen\n\tfor (const screen of screens) {\n\t\tif (directDependentIds.has(screen.id)) {\n\t\t\tcontinue // Skip direct dependents\n\t\t}\n\n\t\tconst path = findPathToDirectDependent(\n\t\t\tscreen.id,\n\t\t\tdirectDependentIds,\n\t\t\tnavigationGraph,\n\t\t\tmaxDepth,\n\t\t\tnew Set(),\n\t\t)\n\n\t\tif (path && !visited.has(screen.id)) {\n\t\t\tvisited.add(screen.id)\n\t\t\ttransitive.push({\n\t\t\t\tscreen,\n\t\t\t\tpath,\n\t\t\t})\n\t\t}\n\t}\n\n\treturn transitive\n}\n\n/**\n * Find a path from a screen to any directly dependent screen using BFS.\n */\nfunction findPathToDirectDependent(\n\tstartId: string,\n\ttargetIds: Set<string>,\n\tgraph: Map<string, Set<string>>,\n\tmaxDepth: number,\n\tvisited: Set<string>,\n): string[] | null {\n\tif (visited.has(startId)) {\n\t\treturn null\n\t}\n\n\tconst queue: Array<{ id: string; path: string[] }> = [\n\t\t{ id: startId, path: [startId] },\n\t]\n\tconst localVisited = new Set<string>([startId])\n\n\twhile (queue.length > 0) {\n\t\tconst current = queue.shift()\n\t\tif (!current) break\n\n\t\tif (current.path.length > maxDepth + 1) {\n\t\t\tcontinue\n\t\t}\n\n\t\tconst neighbors = graph.get(current.id)\n\t\tif (!neighbors) {\n\t\t\tcontinue\n\t\t}\n\n\t\tfor (const neighborId of neighbors) {\n\t\t\tif (localVisited.has(neighborId)) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tconst newPath = [...current.path, neighborId]\n\n\t\t\t// Check if path exceeds maxDepth (path includes start, so maxDepth+1 is the max length)\n\t\t\tif (newPath.length > maxDepth + 1) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif (targetIds.has(neighborId)) {\n\t\t\t\treturn newPath\n\t\t\t}\n\n\t\t\tlocalVisited.add(neighborId)\n\t\t\tqueue.push({ id: neighborId, path: newPath })\n\t\t}\n\t}\n\n\treturn null\n}\n\n/**\n * Analyze the impact of a change to an API on the screen catalog.\n */\nexport function analyzeImpact(\n\tscreens: Screen[],\n\tapiName: string,\n\tmaxDepth = 3,\n): ImpactResult {\n\t// Find direct dependents\n\tconst direct = findDirectDependents(screens, apiName)\n\tconst directIds = new Set(direct.map((s) => s.id))\n\n\t// Find transitive dependents\n\tconst transitive = findTransitiveDependents(screens, directIds, maxDepth)\n\n\treturn {\n\t\tapi: apiName,\n\t\tdirect,\n\t\ttransitive,\n\t\ttotalCount: direct.length + transitive.length,\n\t}\n}\n\n/**\n * Format the impact result as text output.\n */\nexport function formatImpactText(result: ImpactResult): string {\n\tconst lines: string[] = []\n\n\tlines.push(`Impact Analysis: ${result.api}`)\n\tlines.push(\"\")\n\n\tif (result.direct.length > 0) {\n\t\tlines.push(\n\t\t\t`Direct (${result.direct.length} screen${result.direct.length > 1 ? \"s\" : \"\"}):`,\n\t\t)\n\t\tfor (const screen of result.direct) {\n\t\t\tconst owner = screen.owner?.length ? ` [${screen.owner.join(\", \")}]` : \"\"\n\t\t\tlines.push(` - ${screen.id} ${screen.route}${owner}`)\n\t\t}\n\t\tlines.push(\"\")\n\t}\n\n\tif (result.transitive.length > 0) {\n\t\tlines.push(\n\t\t\t`Transitive (${result.transitive.length} screen${result.transitive.length > 1 ? \"s\" : \"\"}):`,\n\t\t)\n\t\tfor (const { path } of result.transitive) {\n\t\t\tlines.push(` - ${path.join(\" -> \")}`)\n\t\t}\n\t\tlines.push(\"\")\n\t}\n\n\tif (result.totalCount === 0) {\n\t\tlines.push(\"No screens depend on this API.\")\n\t\tlines.push(\"\")\n\t} else {\n\t\tlines.push(\n\t\t\t`Total: ${result.totalCount} screen${result.totalCount > 1 ? \"s\" : \"\"} affected`,\n\t\t)\n\t}\n\n\treturn lines.join(\"\\n\")\n}\n\n/**\n * Format the impact result as JSON output.\n */\nexport function formatImpactJson(result: ImpactResult): string {\n\treturn JSON.stringify(\n\t\t{\n\t\t\tapi: result.api,\n\t\t\tsummary: {\n\t\t\t\tdirectCount: result.direct.length,\n\t\t\t\ttransitiveCount: result.transitive.length,\n\t\t\t\ttotalCount: result.totalCount,\n\t\t\t},\n\t\t\tdirect: result.direct.map((s) => ({\n\t\t\t\tid: s.id,\n\t\t\t\ttitle: s.title,\n\t\t\t\troute: s.route,\n\t\t\t\towner: s.owner,\n\t\t\t})),\n\t\t\ttransitive: result.transitive.map(({ screen, path }) => ({\n\t\t\t\tid: screen.id,\n\t\t\t\ttitle: screen.title,\n\t\t\t\troute: screen.route,\n\t\t\t\tpath,\n\t\t\t})),\n\t\t},\n\t\tnull,\n\t\t2,\n\t)\n}\n","import { existsSync, readFileSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport type { Screen } from \"@screenbook/core\"\nimport { define } from \"gunshi\"\nimport { loadConfig } from \"../utils/config.js\"\nimport { ERRORS } from \"../utils/errors.js\"\nimport {\n\tanalyzeImpact,\n\tformatImpactJson,\n\tformatImpactText,\n} from \"../utils/impactAnalysis.js\"\nimport { logger } from \"../utils/logger.js\"\n\nexport const impactCommand = define({\n\tname: \"impact\",\n\tdescription: \"Analyze which screens depend on a specific API/service\",\n\targs: {\n\t\tapi: {\n\t\t\ttype: \"positional\",\n\t\t\tdescription:\n\t\t\t\t\"API or service name to analyze (e.g., InvoiceAPI.getDetail)\",\n\t\t\trequired: true,\n\t\t},\n\t\tconfig: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"c\",\n\t\t\tdescription: \"Path to config file\",\n\t\t},\n\t\tformat: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"f\",\n\t\t\tdescription: \"Output format: text (default) or json\",\n\t\t\tdefault: \"text\",\n\t\t},\n\t\tdepth: {\n\t\t\ttype: \"number\",\n\t\t\tshort: \"d\",\n\t\t\tdescription: \"Maximum depth for transitive dependencies\",\n\t\t\tdefault: 3,\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tconst config = await loadConfig(ctx.values.config)\n\t\tconst cwd = process.cwd()\n\n\t\tconst apiName = ctx.values.api\n\t\tif (!apiName) {\n\t\t\tlogger.errorWithHelp(ERRORS.API_NAME_REQUIRED)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tconst format = ctx.values.format ?? \"text\"\n\t\tconst depth = ctx.values.depth ?? 3\n\n\t\t// Load screens.json\n\t\tconst screensPath = join(cwd, config.outDir, \"screens.json\")\n\n\t\tif (!existsSync(screensPath)) {\n\t\t\tlogger.errorWithHelp(ERRORS.SCREENS_NOT_FOUND)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tlet screens: Screen[]\n\t\ttry {\n\t\t\tconst content = readFileSync(screensPath, \"utf-8\")\n\t\t\tscreens = JSON.parse(content) as Screen[]\n\t\t} catch (error) {\n\t\t\tlogger.errorWithHelp({\n\t\t\t\t...ERRORS.SCREENS_PARSE_ERROR,\n\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t})\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tif (screens.length === 0) {\n\t\t\tlogger.warn(\"No screens found in the catalog.\")\n\t\t\tlogger.blank()\n\t\t\tlogger.log(\"Run 'screenbook generate' to create screen.meta.ts files,\")\n\t\t\tlogger.log(\"then 'screenbook build' to generate the catalog.\")\n\t\t\treturn\n\t\t}\n\n\t\t// Analyze impact\n\t\tconst result = analyzeImpact(screens, apiName, depth)\n\n\t\t// Output result\n\t\tif (format === \"json\") {\n\t\t\tlogger.log(formatImpactJson(result))\n\t\t} else {\n\t\t\tlogger.log(formatImpactText(result))\n\t\t}\n\t},\n})\n","import { existsSync, readFileSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport prompts from \"prompts\"\n\nexport interface FrameworkInfo {\n\tname: string\n\troutesPattern: string\n\tmetaPattern: string\n}\n\ninterface FrameworkDefinition extends FrameworkInfo {\n\tpackages: string[]\n\tconfigFiles: string[]\n\t/**\n\t * Additional check to distinguish variants (e.g., App Router vs Pages Router)\n\t */\n\tcheck?: (cwd: string) => boolean\n}\n\nconst FRAMEWORKS: FrameworkDefinition[] = [\n\t{\n\t\tname: \"Next.js (App Router)\",\n\t\tpackages: [\"next\"],\n\t\tconfigFiles: [\"next.config.js\", \"next.config.mjs\", \"next.config.ts\"],\n\t\troutesPattern: \"app/**/page.tsx\",\n\t\tmetaPattern: \"app/**/screen.meta.ts\",\n\t\tcheck: (cwd) =>\n\t\t\texistsSync(join(cwd, \"app\")) || existsSync(join(cwd, \"src/app\")),\n\t},\n\t{\n\t\tname: \"Next.js (Pages Router)\",\n\t\tpackages: [\"next\"],\n\t\tconfigFiles: [\"next.config.js\", \"next.config.mjs\", \"next.config.ts\"],\n\t\troutesPattern: \"pages/**/*.tsx\",\n\t\tmetaPattern: \"pages/**/screen.meta.ts\",\n\t\tcheck: (cwd) =>\n\t\t\texistsSync(join(cwd, \"pages\")) || existsSync(join(cwd, \"src/pages\")),\n\t},\n\t{\n\t\tname: \"Remix\",\n\t\tpackages: [\"@remix-run/react\", \"remix\"],\n\t\tconfigFiles: [\"remix.config.js\", \"vite.config.ts\"],\n\t\troutesPattern: \"app/routes/**/*.tsx\",\n\t\tmetaPattern: \"app/routes/**/screen.meta.ts\",\n\t},\n\t{\n\t\tname: \"Nuxt\",\n\t\tpackages: [\"nuxt\"],\n\t\tconfigFiles: [\"nuxt.config.ts\", \"nuxt.config.js\", \"nuxt.config.mjs\"],\n\t\troutesPattern: \"pages/**/*.vue\",\n\t\tmetaPattern: \"pages/**/screen.meta.ts\",\n\t\tcheck: (cwd) => {\n\t\t\t// Nuxt 4 uses app/pages, Nuxt 3 uses pages\n\t\t\tif (existsSync(join(cwd, \"app/pages\"))) {\n\t\t\t\treturn false // Will be handled by Nuxt 4 definition\n\t\t\t}\n\t\t\treturn existsSync(join(cwd, \"pages\"))\n\t\t},\n\t},\n\t{\n\t\tname: \"Nuxt 4\",\n\t\tpackages: [\"nuxt\"],\n\t\tconfigFiles: [\"nuxt.config.ts\", \"nuxt.config.js\"],\n\t\troutesPattern: \"app/pages/**/*.vue\",\n\t\tmetaPattern: \"app/pages/**/screen.meta.ts\",\n\t\tcheck: (cwd) => existsSync(join(cwd, \"app/pages\")),\n\t},\n\t{\n\t\tname: \"Astro\",\n\t\tpackages: [\"astro\"],\n\t\tconfigFiles: [\n\t\t\t\"astro.config.mjs\",\n\t\t\t\"astro.config.js\",\n\t\t\t\"astro.config.ts\",\n\t\t\t\"astro.config.cjs\",\n\t\t],\n\t\troutesPattern: \"src/pages/**/*.astro\",\n\t\tmetaPattern: \"src/pages/**/screen.meta.ts\",\n\t},\n\t{\n\t\tname: \"Vite + Vue\",\n\t\tpackages: [\"vite\", \"vue\"],\n\t\tconfigFiles: [\"vite.config.ts\", \"vite.config.js\", \"vite.config.mjs\"],\n\t\troutesPattern: \"src/pages/**/*.vue\",\n\t\tmetaPattern: \"src/pages/**/screen.meta.ts\",\n\t\t// Check that react is NOT present to avoid matching React projects\n\t\tcheck: (cwd) => {\n\t\t\tconst packageJson = readPackageJson(cwd)\n\t\t\tif (!packageJson) return false\n\t\t\treturn !hasPackage(packageJson, \"react\")\n\t\t},\n\t},\n\t{\n\t\tname: \"Vite + React\",\n\t\tpackages: [\"vite\", \"react\"],\n\t\tconfigFiles: [\"vite.config.ts\", \"vite.config.js\", \"vite.config.mjs\"],\n\t\troutesPattern: \"src/pages/**/*.tsx\",\n\t\tmetaPattern: \"src/pages/**/screen.meta.ts\",\n\t},\n]\n\ninterface PackageJson {\n\tdependencies?: Record<string, string>\n\tdevDependencies?: Record<string, string>\n}\n\nfunction readPackageJson(cwd: string): PackageJson | null {\n\tconst packageJsonPath = join(cwd, \"package.json\")\n\tif (!existsSync(packageJsonPath)) {\n\t\treturn null\n\t}\n\ttry {\n\t\tconst content = readFileSync(packageJsonPath, \"utf-8\")\n\t\treturn JSON.parse(content)\n\t} catch {\n\t\treturn null\n\t}\n}\n\nfunction hasPackage(packageJson: PackageJson, packageName: string): boolean {\n\treturn !!(\n\t\tpackageJson.dependencies?.[packageName] ||\n\t\tpackageJson.devDependencies?.[packageName]\n\t)\n}\n\nfunction hasConfigFile(cwd: string, configFiles: string[]): boolean {\n\treturn configFiles.some((file) => existsSync(join(cwd, file)))\n}\n\n/**\n * Auto-detect the frontend framework in a project directory.\n * Returns framework info if detected, null otherwise.\n */\nexport function detectFramework(cwd: string): FrameworkInfo | null {\n\tconst packageJson = readPackageJson(cwd)\n\tif (!packageJson) {\n\t\treturn null\n\t}\n\n\tfor (const framework of FRAMEWORKS) {\n\t\t// Check if required packages are present\n\t\tconst hasRequiredPackage = framework.packages.some((pkg) =>\n\t\t\thasPackage(packageJson, pkg),\n\t\t)\n\t\tif (!hasRequiredPackage) {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Check for config files\n\t\tconst hasConfig = hasConfigFile(cwd, framework.configFiles)\n\t\tif (!hasConfig) {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Run additional check if defined\n\t\tif (framework.check && !framework.check(cwd)) {\n\t\t\tcontinue\n\t\t}\n\n\t\treturn {\n\t\t\tname: framework.name,\n\t\t\troutesPattern: framework.routesPattern,\n\t\t\tmetaPattern: framework.metaPattern,\n\t\t}\n\t}\n\n\treturn null\n}\n\n/**\n * Interactive framework selection when auto-detection fails.\n */\nexport async function promptFrameworkSelection(): Promise<FrameworkInfo | null> {\n\tconst choices = FRAMEWORKS.filter(\n\t\t// Remove duplicates (e.g., Nuxt 4 vs Nuxt)\n\t\t(fw, idx, arr) =>\n\t\t\tarr.findIndex((f) => f.routesPattern === fw.routesPattern) === idx,\n\t).map((fw) => ({\n\t\ttitle: fw.name,\n\t\tvalue: fw,\n\t}))\n\n\tchoices.push({\n\t\ttitle: \"Other (manual configuration)\",\n\t\tvalue: null as unknown as FrameworkDefinition,\n\t})\n\n\tconst response = await prompts({\n\t\ttype: \"select\",\n\t\tname: \"framework\",\n\t\tmessage: \"Select your frontend framework:\",\n\t\tchoices,\n\t})\n\n\tif (!response.framework) {\n\t\treturn null\n\t}\n\n\treturn {\n\t\tname: response.framework.name,\n\t\troutesPattern: response.framework.routesPattern,\n\t\tmetaPattern: response.framework.metaPattern,\n\t}\n}\n\n/**\n * Detect framework or prompt user if detection fails.\n */\nexport async function detectOrPromptFramework(\n\tcwd: string,\n): Promise<FrameworkInfo | null> {\n\tconst detected = detectFramework(cwd)\n\tif (detected) {\n\t\treturn detected\n\t}\n\treturn promptFrameworkSelection()\n}\n","import { existsSync, readFileSync, writeFileSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport { define } from \"gunshi\"\nimport {\n\tdetectFramework,\n\ttype FrameworkInfo,\n\tpromptFrameworkSelection,\n} from \"../utils/detectFramework.js\"\nimport { logger } from \"../utils/logger.js\"\n\nfunction generateConfigTemplate(framework: FrameworkInfo | null): string {\n\tif (framework) {\n\t\treturn `import { defineConfig } from \"@screenbook/core\"\n\nexport default defineConfig({\n\t// Auto-detected: ${framework.name}\n\tmetaPattern: \"${framework.metaPattern}\",\n\troutesPattern: \"${framework.routesPattern}\",\n\toutDir: \".screenbook\",\n})\n`\n\t}\n\n\t// Fallback template when no framework detected\n\treturn `import { defineConfig } from \"@screenbook/core\"\n\nexport default defineConfig({\n\t// Glob pattern for screen metadata files\n\tmetaPattern: \"src/**/screen.meta.ts\",\n\n\t// Glob pattern for route files (uncomment and adjust for your framework):\n\t// routesPattern: \"src/pages/**/page.tsx\", // Vite/React\n\t// routesPattern: \"app/**/page.tsx\", // Next.js App Router\n\t// routesPattern: \"pages/**/*.tsx\", // Next.js Pages Router\n\t// routesPattern: \"app/routes/**/*.tsx\", // Remix\n\t// routesPattern: \"pages/**/*.vue\", // Nuxt\n\t// routesPattern: \"src/pages/**/*.astro\", // Astro\n\n\toutDir: \".screenbook\",\n})\n`\n}\n\nfunction printValueProposition(): void {\n\tlogger.blank()\n\tlogger.log(logger.bold(\"What Screenbook gives you:\"))\n\tlogger.log(\" - Screen catalog with search & filter\")\n\tlogger.log(\" - Navigation graph visualization\")\n\tlogger.log(\" - Impact analysis (API -> affected screens)\")\n\tlogger.log(\" - CI lint for documentation coverage\")\n}\n\nfunction printNextSteps(hasRoutesPattern: boolean): void {\n\tlogger.blank()\n\tlogger.log(logger.bold(\"Next steps:\"))\n\tif (hasRoutesPattern) {\n\t\tlogger.log(\n\t\t\t` 1. Run ${logger.code(\"screenbook generate\")} to auto-create screen.meta.ts files`,\n\t\t)\n\t\tlogger.log(\n\t\t\t` 2. Run ${logger.code(\"screenbook dev\")} to start the UI server`,\n\t\t)\n\t} else {\n\t\tlogger.log(\" 1. Configure routesPattern in screenbook.config.ts\")\n\t\tlogger.log(\n\t\t\t` 2. Run ${logger.code(\"screenbook generate\")} to auto-create screen.meta.ts files`,\n\t\t)\n\t\tlogger.log(\n\t\t\t` 3. Run ${logger.code(\"screenbook dev\")} to start the UI server`,\n\t\t)\n\t}\n\tlogger.blank()\n\tlogger.log(\"screen.meta.ts files are created alongside your route files:\")\n\tlogger.blank()\n\tlogger.log(logger.dim(\" src/pages/dashboard/\"))\n\tlogger.log(logger.dim(\" page.tsx # Your route file\"))\n\tlogger.log(\n\t\tlogger.dim(\" screen.meta.ts # Auto-generated, customize as needed\"),\n\t)\n}\n\nexport const initCommand = define({\n\tname: \"init\",\n\tdescription: \"Initialize Screenbook in a project\",\n\targs: {\n\t\tforce: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"f\",\n\t\t\tdescription: \"Overwrite existing files\",\n\t\t\tdefault: false,\n\t\t},\n\t\tskipDetect: {\n\t\t\ttype: \"boolean\",\n\t\t\tdescription: \"Skip framework auto-detection\",\n\t\t\tdefault: false,\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tconst cwd = process.cwd()\n\t\tconst force = ctx.values.force ?? false\n\t\tconst skipDetect = ctx.values.skipDetect ?? false\n\n\t\tlogger.info(\"Initializing Screenbook...\")\n\t\tlogger.blank()\n\n\t\t// Framework detection\n\t\tlet framework: FrameworkInfo | null = null\n\n\t\tif (!skipDetect) {\n\t\t\tframework = detectFramework(cwd)\n\n\t\t\tif (framework) {\n\t\t\t\tlogger.itemSuccess(`Detected: ${framework.name}`)\n\t\t\t} else {\n\t\t\t\tlogger.log(\" Could not auto-detect framework\")\n\t\t\t\tlogger.blank()\n\t\t\t\tframework = await promptFrameworkSelection()\n\n\t\t\t\tif (framework) {\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tlogger.itemSuccess(`Selected: ${framework.name}`)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Create screenbook.config.ts\n\t\tconst configPath = join(cwd, \"screenbook.config.ts\")\n\t\tif (!force && existsSync(configPath)) {\n\t\t\tlogger.log(\n\t\t\t\t` ${logger.dim(\"-\")} screenbook.config.ts already exists ${logger.dim(\"(skipped)\")}`,\n\t\t\t)\n\t\t} else {\n\t\t\tconst configContent = generateConfigTemplate(framework)\n\t\t\twriteFileSync(configPath, configContent)\n\t\t\tlogger.itemSuccess(\"Created screenbook.config.ts\")\n\t\t}\n\n\t\t// Update .gitignore\n\t\tconst gitignorePath = join(cwd, \".gitignore\")\n\t\tconst screenbookIgnore = \".screenbook\"\n\n\t\tif (existsSync(gitignorePath)) {\n\t\t\tconst gitignoreContent = readFileSync(gitignorePath, \"utf-8\")\n\t\t\tif (!gitignoreContent.includes(screenbookIgnore)) {\n\t\t\t\tconst newContent = `${gitignoreContent.trimEnd()}\\n\\n# Screenbook\\n${screenbookIgnore}\\n`\n\t\t\t\twriteFileSync(gitignorePath, newContent)\n\t\t\t\tlogger.itemSuccess(\"Added .screenbook to .gitignore\")\n\t\t\t} else {\n\t\t\t\tlogger.log(\n\t\t\t\t\t` ${logger.dim(\"-\")} .screenbook already in .gitignore ${logger.dim(\"(skipped)\")}`,\n\t\t\t\t)\n\t\t\t}\n\t\t} else {\n\t\t\twriteFileSync(gitignorePath, `# Screenbook\\n${screenbookIgnore}\\n`)\n\t\t\tlogger.itemSuccess(\"Created .gitignore with .screenbook\")\n\t\t}\n\n\t\tlogger.blank()\n\t\tlogger.done(\"Screenbook initialized successfully!\")\n\n\t\tprintValueProposition()\n\t\tprintNextSteps(framework !== null)\n\t},\n})\n","import { existsSync, readFileSync } from \"node:fs\"\nimport { dirname, join } from \"node:path\"\nimport type { Screen } from \"@screenbook/core\"\nimport { define } from \"gunshi\"\nimport { minimatch } from \"minimatch\"\nimport { glob } from \"tinyglobby\"\nimport { loadConfig } from \"../utils/config.js\"\nimport {\n\tdetectCycles,\n\tformatCycleWarnings,\n\tgetCycleSummary,\n} from \"../utils/cycleDetection.js\"\nimport { ERRORS } from \"../utils/errors.js\"\nimport { logger } from \"../utils/logger.js\"\n\nexport const lintCommand = define({\n\tname: \"lint\",\n\tdescription: \"Detect routes without screen.meta.ts files\",\n\targs: {\n\t\tconfig: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"c\",\n\t\t\tdescription: \"Path to config file\",\n\t\t},\n\t\tallowCycles: {\n\t\t\ttype: \"boolean\",\n\t\t\tdescription: \"Suppress circular navigation warnings\",\n\t\t\tdefault: false,\n\t\t},\n\t\tstrict: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"s\",\n\t\t\tdescription: \"Fail on disallowed cycles\",\n\t\t\tdefault: false,\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tconst config = await loadConfig(ctx.values.config)\n\t\tconst cwd = process.cwd()\n\t\tconst adoption = config.adoption ?? { mode: \"full\" }\n\t\tlet hasWarnings = false\n\n\t\tif (!config.routesPattern) {\n\t\t\tlogger.errorWithHelp(ERRORS.ROUTES_PATTERN_MISSING)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tlogger.info(\"Linting screen metadata coverage...\")\n\t\tif (adoption.mode === \"progressive\") {\n\t\t\tlogger.log(`Mode: Progressive adoption`)\n\t\t\tif (adoption.includePatterns?.length) {\n\t\t\t\tlogger.log(`Checking: ${adoption.includePatterns.join(\", \")}`)\n\t\t\t}\n\t\t\tif (adoption.minimumCoverage != null) {\n\t\t\t\tlogger.log(`Minimum coverage: ${adoption.minimumCoverage}%`)\n\t\t\t}\n\t\t}\n\t\tlogger.blank()\n\n\t\t// Find all route files\n\t\tlet routeFiles = await glob(config.routesPattern, {\n\t\t\tcwd,\n\t\t\tignore: config.ignore,\n\t\t})\n\n\t\t// In progressive mode, filter to only included patterns\n\t\tif (adoption.mode === \"progressive\" && adoption.includePatterns?.length) {\n\t\t\trouteFiles = routeFiles.filter((file) =>\n\t\t\t\tadoption.includePatterns?.some((pattern) => minimatch(file, pattern)),\n\t\t\t)\n\t\t}\n\n\t\tif (routeFiles.length === 0) {\n\t\t\tlogger.warn(`No route files found matching: ${config.routesPattern}`)\n\t\t\tif (adoption.mode === \"progressive\" && adoption.includePatterns?.length) {\n\t\t\t\tlogger.log(\n\t\t\t\t\t` ${logger.dim(`(filtered by includePatterns: ${adoption.includePatterns.join(\", \")})`)}`,\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\t// Find all screen.meta.ts files\n\t\tconst metaFiles = await glob(config.metaPattern, {\n\t\t\tcwd,\n\t\t\tignore: config.ignore,\n\t\t})\n\n\t\t// Build a set of directories that have screen.meta.ts\n\t\tconst metaDirs = new Set<string>()\n\t\tfor (const metaFile of metaFiles) {\n\t\t\tmetaDirs.add(dirname(metaFile))\n\t\t}\n\n\t\t// Check each route file - simple colocation check\n\t\tconst missingMeta: string[] = []\n\t\tconst covered: string[] = []\n\n\t\tfor (const routeFile of routeFiles) {\n\t\t\tconst routeDir = dirname(routeFile)\n\n\t\t\t// Check if there's a screen.meta.ts in the same directory\n\t\t\tif (metaDirs.has(routeDir)) {\n\t\t\t\tcovered.push(routeFile)\n\t\t\t} else {\n\t\t\t\tmissingMeta.push(routeFile)\n\t\t\t}\n\t\t}\n\n\t\t// Report results\n\t\tconst total = routeFiles.length\n\t\tconst coveredCount = covered.length\n\t\tconst missingCount = missingMeta.length\n\t\tconst coveragePercent = Math.round((coveredCount / total) * 100)\n\n\t\tlogger.log(`Found ${total} route files`)\n\t\tlogger.log(`Coverage: ${coveredCount}/${total} (${coveragePercent}%)`)\n\t\tlogger.blank()\n\n\t\t// Determine if lint should fail\n\t\tconst minimumCoverage = adoption.minimumCoverage ?? 100\n\t\tconst passedCoverage = coveragePercent >= minimumCoverage\n\n\t\tif (missingCount > 0) {\n\t\t\tlogger.log(`Missing screen.meta.ts (${missingCount} files):`)\n\t\t\tlogger.blank()\n\n\t\t\tfor (const file of missingMeta) {\n\t\t\t\tconst suggestedMetaPath = join(dirname(file), \"screen.meta.ts\")\n\t\t\t\tlogger.itemError(file)\n\t\t\t\tlogger.log(` ${logger.dim(\"→\")} ${logger.path(suggestedMetaPath)}`)\n\t\t\t}\n\n\t\t\tlogger.blank()\n\t\t}\n\n\t\tif (!passedCoverage) {\n\t\t\tlogger.error(\n\t\t\t\t`Lint failed: Coverage ${coveragePercent}% is below minimum ${minimumCoverage}%`,\n\t\t\t)\n\t\t\tprocess.exit(1)\n\t\t} else if (missingCount > 0) {\n\t\t\tlogger.success(\n\t\t\t\t`Coverage ${coveragePercent}% meets minimum ${minimumCoverage}%`,\n\t\t\t)\n\t\t\tif (adoption.mode === \"progressive\") {\n\t\t\t\tlogger.log(\n\t\t\t\t\t` ${logger.dim(\"Tip:\")} Increase minimumCoverage in config to gradually improve coverage`,\n\t\t\t\t)\n\t\t\t}\n\t\t} else {\n\t\t\tlogger.done(\"All routes have screen.meta.ts files\")\n\t\t}\n\n\t\t// Check for orphan screens (unreachable screens) and cycles\n\t\tconst screensPath = join(cwd, config.outDir, \"screens.json\")\n\t\tif (existsSync(screensPath)) {\n\t\t\ttry {\n\t\t\t\tconst content = readFileSync(screensPath, \"utf-8\")\n\t\t\t\tconst screens = JSON.parse(content) as Screen[]\n\n\t\t\t\t// Check for orphan screens\n\t\t\t\tconst orphans = findOrphanScreens(screens)\n\n\t\t\t\tif (orphans.length > 0) {\n\t\t\t\t\thasWarnings = true\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tlogger.warn(`Orphan screens detected (${orphans.length}):`)\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tlogger.log(\" These screens have no entryPoints and are not\")\n\t\t\t\t\tlogger.log(\" referenced in any other screen's 'next' array.\")\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tfor (const orphan of orphans) {\n\t\t\t\t\t\tlogger.itemWarn(`${orphan.id} ${logger.dim(orphan.route)}`)\n\t\t\t\t\t}\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t` ${logger.dim(\"Consider adding entryPoints or removing these screens.\")}`,\n\t\t\t\t\t)\n\t\t\t\t}\n\n\t\t\t\t// Check for circular navigation\n\t\t\t\tif (!ctx.values.allowCycles) {\n\t\t\t\t\tconst cycleResult = detectCycles(screens)\n\t\t\t\t\tif (cycleResult.hasCycles) {\n\t\t\t\t\t\thasWarnings = true\n\t\t\t\t\t\tlogger.blank()\n\t\t\t\t\t\tlogger.warn(getCycleSummary(cycleResult))\n\t\t\t\t\t\tlogger.log(formatCycleWarnings(cycleResult.cycles))\n\t\t\t\t\t\tlogger.blank()\n\t\t\t\t\t\tif (cycleResult.disallowedCycles.length > 0) {\n\t\t\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t\t\t` ${logger.dim(\"Use 'allowCycles: true' in screen.meta.ts to allow intentional cycles.\")}`,\n\t\t\t\t\t\t\t)\n\n\t\t\t\t\t\t\tif (ctx.values.strict) {\n\t\t\t\t\t\t\t\tlogger.blank()\n\t\t\t\t\t\t\t\tlogger.errorWithHelp(\n\t\t\t\t\t\t\t\t\tERRORS.CYCLES_DETECTED(cycleResult.disallowedCycles.length),\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\tprocess.exit(1)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Check for invalid navigation references\n\t\t\t\tconst invalidNavs = findInvalidNavigations(screens)\n\t\t\t\tif (invalidNavs.length > 0) {\n\t\t\t\t\thasWarnings = true\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tlogger.warn(`Invalid navigation targets (${invalidNavs.length}):`)\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t\" These navigation references point to non-existent screens.\",\n\t\t\t\t\t)\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tfor (const inv of invalidNavs) {\n\t\t\t\t\t\tlogger.itemWarn(\n\t\t\t\t\t\t\t`${inv.screenId} → ${logger.dim(inv.field)}: \"${inv.target}\"`,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t` ${logger.dim(\"Check that these screen IDs exist in your codebase.\")}`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\t// Handle specific error types\n\t\t\t\tif (error instanceof SyntaxError) {\n\t\t\t\t\tlogger.warn(\"Failed to parse screens.json - file may be corrupted\")\n\t\t\t\t\tlogger.log(` ${logger.dim(\"Run 'screenbook build' to regenerate.\")}`)\n\t\t\t\t\thasWarnings = true\n\t\t\t\t} else if (error instanceof Error) {\n\t\t\t\t\tlogger.warn(`Failed to analyze screens.json: ${error.message}`)\n\t\t\t\t\thasWarnings = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (hasWarnings) {\n\t\t\tlogger.blank()\n\t\t\tlogger.warn(\"Lint completed with warnings.\")\n\t\t}\n\t},\n})\n\ninterface InvalidNavigation {\n\tscreenId: string\n\tfield: string\n\ttarget: string\n}\n\n/**\n * Find navigation references that point to non-existent screens.\n * Checks `next`, `entryPoints` arrays and mock navigation targets.\n */\nfunction findInvalidNavigations(screens: Screen[]): InvalidNavigation[] {\n\tconst screenIds = new Set(screens.map((s) => s.id))\n\tconst invalid: InvalidNavigation[] = []\n\n\tfor (const screen of screens) {\n\t\t// Check next array\n\t\tif (screen.next) {\n\t\t\tfor (const target of screen.next) {\n\t\t\t\tif (!screenIds.has(target)) {\n\t\t\t\t\tinvalid.push({ screenId: screen.id, field: \"next\", target })\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Check entryPoints array\n\t\tif (screen.entryPoints) {\n\t\t\tfor (const target of screen.entryPoints) {\n\t\t\t\tif (!screenIds.has(target)) {\n\t\t\t\t\tinvalid.push({ screenId: screen.id, field: \"entryPoints\", target })\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn invalid\n}\n\n/**\n * Find screens that are unreachable (orphans).\n * A screen is an orphan if:\n * - It has no entryPoints defined\n * - AND it's not referenced in any other screen's `next` array\n */\nfunction findOrphanScreens(screens: Screen[]): Screen[] {\n\t// Build a set of all screen IDs that are referenced in `next` arrays\n\tconst referencedIds = new Set<string>()\n\tfor (const screen of screens) {\n\t\tif (screen.next) {\n\t\t\tfor (const nextId of screen.next) {\n\t\t\t\treferencedIds.add(nextId)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Find orphan screens\n\tconst orphans: Screen[] = []\n\tfor (const screen of screens) {\n\t\tconst hasEntryPoints = screen.entryPoints && screen.entryPoints.length > 0\n\t\tconst isReferenced = referencedIds.has(screen.id)\n\n\t\t// A screen is an orphan if it has no entry points AND is not referenced\n\t\tif (!hasEntryPoints && !isReferenced) {\n\t\t\torphans.push(screen)\n\t\t}\n\t}\n\n\treturn orphans\n}\n","import { basename, dirname } from \"node:path\"\nimport type { ImpactResult } from \"./impactAnalysis.js\"\n\n/**\n * Extract potential API names from changed file paths.\n * Looks for common API file patterns.\n */\nexport function extractApiNames(files: string[]): string[] {\n\tconst apis = new Set<string>()\n\n\tfor (const file of files) {\n\t\tconst fileName = basename(file, \".ts\")\n\t\t\t.replace(/\\.tsx?$/, \"\")\n\t\t\t.replace(/\\.js$/, \"\")\n\t\t\t.replace(/\\.jsx?$/, \"\")\n\n\t\tconst dirName = basename(dirname(file))\n\n\t\t// Pattern: src/api/InvoiceAPI.ts -> InvoiceAPI\n\t\tif (\n\t\t\tfile.includes(\"/api/\") ||\n\t\t\tfile.includes(\"/apis/\") ||\n\t\t\tfile.includes(\"/services/\")\n\t\t) {\n\t\t\tif (\n\t\t\t\tfileName.endsWith(\"API\") ||\n\t\t\t\tfileName.endsWith(\"Api\") ||\n\t\t\t\tfileName.endsWith(\"Service\")\n\t\t\t) {\n\t\t\t\tapis.add(fileName)\n\t\t\t}\n\t\t}\n\n\t\t// Pattern: src/services/invoice/index.ts -> InvoiceService\n\t\tif (\n\t\t\tfile.includes(\"/services/\") &&\n\t\t\t(fileName === \"index\" || fileName === dirName)\n\t\t) {\n\t\t\tconst serviceName = `${capitalize(dirName)}Service`\n\t\t\tapis.add(serviceName)\n\t\t}\n\n\t\t// Pattern: src/api/invoice.ts -> InvoiceAPI\n\t\tif (file.includes(\"/api/\") || file.includes(\"/apis/\")) {\n\t\t\tif (!fileName.endsWith(\"API\") && !fileName.endsWith(\"Api\")) {\n\t\t\t\tconst apiName = `${capitalize(fileName)}API`\n\t\t\t\tapis.add(apiName)\n\t\t\t}\n\t\t}\n\n\t\t// Pattern: explicit API/Service file names\n\t\tif (\n\t\t\tfileName.toLowerCase().includes(\"api\") ||\n\t\t\tfileName.toLowerCase().includes(\"service\")\n\t\t) {\n\t\t\tapis.add(fileName)\n\t\t}\n\t}\n\n\treturn Array.from(apis).sort()\n}\n\nexport function capitalize(str: string): string {\n\treturn str.charAt(0).toUpperCase() + str.slice(1)\n}\n\n/**\n * Format the results as Markdown for PR comments.\n */\nexport function formatMarkdown(\n\tchangedFiles: string[],\n\tdetectedApis: string[],\n\tresults: ImpactResult[],\n): string {\n\tconst lines: string[] = []\n\n\tlines.push(\"## Screenbook Impact Analysis\")\n\tlines.push(\"\")\n\n\tif (results.length === 0) {\n\t\tlines.push(\"No screen impacts detected from the API changes in this PR.\")\n\t\tlines.push(\"\")\n\t\tlines.push(\"<details>\")\n\t\tlines.push(\"<summary>Detected APIs (no screen dependencies)</summary>\")\n\t\tlines.push(\"\")\n\t\tfor (const api of detectedApis) {\n\t\t\tlines.push(`- \\`${api}\\``)\n\t\t}\n\t\tlines.push(\"\")\n\t\tlines.push(\"</details>\")\n\t\treturn lines.join(\"\\n\")\n\t}\n\n\t// Summary\n\tconst totalDirect = results.reduce((sum, r) => sum + r.direct.length, 0)\n\tconst totalTransitive = results.reduce(\n\t\t(sum, r) => sum + r.transitive.length,\n\t\t0,\n\t)\n\tconst totalScreens = totalDirect + totalTransitive\n\n\tlines.push(\n\t\t`**${totalScreens} screen${totalScreens > 1 ? \"s\" : \"\"} affected** by changes to ${results.length} API${results.length > 1 ? \"s\" : \"\"}`,\n\t)\n\tlines.push(\"\")\n\n\t// Per-API breakdown\n\tfor (const result of results) {\n\t\tlines.push(`### ${result.api}`)\n\t\tlines.push(\"\")\n\n\t\tif (result.direct.length > 0) {\n\t\t\tlines.push(`**Direct dependencies** (${result.direct.length}):`)\n\t\t\tlines.push(\"\")\n\t\t\tlines.push(\"| Screen | Route | Owner |\")\n\t\t\tlines.push(\"|--------|-------|-------|\")\n\t\t\tfor (const screen of result.direct) {\n\t\t\t\tconst owner = screen.owner?.join(\", \") ?? \"-\"\n\t\t\t\tlines.push(`| ${screen.id} | \\`${screen.route}\\` | ${owner} |`)\n\t\t\t}\n\t\t\tlines.push(\"\")\n\t\t}\n\n\t\tif (result.transitive.length > 0) {\n\t\t\tlines.push(`**Transitive dependencies** (${result.transitive.length}):`)\n\t\t\tlines.push(\"\")\n\t\t\tfor (const { path } of result.transitive) {\n\t\t\t\tlines.push(`- ${path.join(\" → \")}`)\n\t\t\t}\n\t\t\tlines.push(\"\")\n\t\t}\n\t}\n\n\t// Changed files summary\n\tlines.push(\"<details>\")\n\tlines.push(`<summary>Changed files (${changedFiles.length})</summary>`)\n\tlines.push(\"\")\n\tfor (const file of changedFiles.slice(0, 20)) {\n\t\tlines.push(`- \\`${file}\\``)\n\t}\n\tif (changedFiles.length > 20) {\n\t\tlines.push(`- ... and ${changedFiles.length - 20} more`)\n\t}\n\tlines.push(\"\")\n\tlines.push(\"</details>\")\n\n\treturn lines.join(\"\\n\")\n}\n","import { execSync } from \"node:child_process\"\nimport { existsSync, readFileSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport type { Screen } from \"@screenbook/core\"\nimport { define } from \"gunshi\"\nimport { loadConfig } from \"../utils/config.js\"\nimport { ERRORS } from \"../utils/errors.js\"\nimport { analyzeImpact, type ImpactResult } from \"../utils/impactAnalysis.js\"\nimport { logger } from \"../utils/logger.js\"\nimport { extractApiNames, formatMarkdown } from \"../utils/prImpact.js\"\n\nexport const prImpactCommand = define({\n\tname: \"pr-impact\",\n\tdescription: \"Analyze impact of changed files in a PR\",\n\targs: {\n\t\tbase: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"b\",\n\t\t\tdescription: \"Base branch to compare against (default: main)\",\n\t\t\tdefault: \"main\",\n\t\t},\n\t\tconfig: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"c\",\n\t\t\tdescription: \"Path to config file\",\n\t\t},\n\t\tformat: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"f\",\n\t\t\tdescription: \"Output format: markdown (default) or json\",\n\t\t\tdefault: \"markdown\",\n\t\t},\n\t\tdepth: {\n\t\t\ttype: \"number\",\n\t\t\tshort: \"d\",\n\t\t\tdescription: \"Maximum depth for transitive dependencies\",\n\t\t\tdefault: 3,\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tconst config = await loadConfig(ctx.values.config)\n\t\tconst cwd = process.cwd()\n\n\t\tconst baseBranch = ctx.values.base ?? \"main\"\n\t\tconst format = ctx.values.format ?? \"markdown\"\n\t\tconst depth = ctx.values.depth ?? 3\n\n\t\t// Get changed files from git\n\t\tlet changedFiles: string[]\n\t\ttry {\n\t\t\tconst gitOutput = execSync(\n\t\t\t\t`git diff --name-only ${baseBranch}...HEAD 2>/dev/null || git diff --name-only ${baseBranch} HEAD`,\n\t\t\t\t{ cwd, encoding: \"utf-8\" },\n\t\t\t)\n\t\t\tchangedFiles = gitOutput\n\t\t\t\t.split(\"\\n\")\n\t\t\t\t.map((f) => f.trim())\n\t\t\t\t.filter((f) => f.length > 0)\n\t\t} catch {\n\t\t\tlogger.errorWithHelp(ERRORS.GIT_CHANGED_FILES_ERROR(baseBranch))\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tif (changedFiles.length === 0) {\n\t\t\tlogger.info(\"No changed files found.\")\n\t\t\treturn\n\t\t}\n\n\t\t// Extract potential API names from changed files\n\t\tconst apiNames = extractApiNames(changedFiles)\n\n\t\tif (apiNames.length === 0) {\n\t\t\tif (format === \"markdown\") {\n\t\t\t\tlogger.log(\"## Screenbook Impact Analysis\")\n\t\t\t\tlogger.blank()\n\t\t\t\tlogger.log(\"No API-related changes detected in this PR.\")\n\t\t\t\tlogger.blank()\n\t\t\t\tlogger.log(`Changed files: ${changedFiles.length}`)\n\t\t\t} else {\n\t\t\t\tlogger.log(\n\t\t\t\t\tJSON.stringify({ apis: [], results: [], changedFiles }, null, 2),\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\t// Load screens.json\n\t\tconst screensPath = join(cwd, config.outDir, \"screens.json\")\n\n\t\tif (!existsSync(screensPath)) {\n\t\t\tlogger.errorWithHelp(ERRORS.SCREENS_NOT_FOUND)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tlet screens: Screen[]\n\t\ttry {\n\t\t\tconst content = readFileSync(screensPath, \"utf-8\")\n\t\t\tscreens = JSON.parse(content) as Screen[]\n\t\t} catch (error) {\n\t\t\tlogger.errorWithHelp({\n\t\t\t\t...ERRORS.SCREENS_PARSE_ERROR,\n\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t})\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\t// Analyze impact for each API\n\t\tconst results: ImpactResult[] = []\n\t\tfor (const apiName of apiNames) {\n\t\t\tconst result = analyzeImpact(screens, apiName, depth)\n\t\t\tif (result.totalCount > 0) {\n\t\t\t\tresults.push(result)\n\t\t\t}\n\t\t}\n\n\t\t// Output results\n\t\tif (format === \"json\") {\n\t\t\tlogger.log(\n\t\t\t\tJSON.stringify(\n\t\t\t\t\t{\n\t\t\t\t\t\tchangedFiles,\n\t\t\t\t\t\tdetectedApis: apiNames,\n\t\t\t\t\t\tresults: results.map((r) => ({\n\t\t\t\t\t\t\tapi: r.api,\n\t\t\t\t\t\t\tdirectCount: r.direct.length,\n\t\t\t\t\t\t\ttransitiveCount: r.transitive.length,\n\t\t\t\t\t\t\ttotalCount: r.totalCount,\n\t\t\t\t\t\t\tdirect: r.direct.map((s) => ({\n\t\t\t\t\t\t\t\tid: s.id,\n\t\t\t\t\t\t\t\ttitle: s.title,\n\t\t\t\t\t\t\t\troute: s.route,\n\t\t\t\t\t\t\t\towner: s.owner,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t\ttransitive: r.transitive.map(({ screen, path }) => ({\n\t\t\t\t\t\t\t\tid: screen.id,\n\t\t\t\t\t\t\t\ttitle: screen.title,\n\t\t\t\t\t\t\t\troute: screen.route,\n\t\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t})),\n\t\t\t\t\t},\n\t\t\t\t\tnull,\n\t\t\t\t\t2,\n\t\t\t\t),\n\t\t\t)\n\t\t} else {\n\t\t\tlogger.log(formatMarkdown(changedFiles, apiNames, results))\n\t\t}\n\t},\n})\n","#!/usr/bin/env node\n\nimport { cli, define } from \"gunshi\"\nimport { buildCommand } from \"./commands/build.js\"\nimport { devCommand } from \"./commands/dev.js\"\nimport { doctorCommand } from \"./commands/doctor.js\"\nimport { generateCommand } from \"./commands/generate.js\"\nimport { impactCommand } from \"./commands/impact.js\"\nimport { initCommand } from \"./commands/init.js\"\nimport { lintCommand } from \"./commands/lint.js\"\nimport { prImpactCommand } from \"./commands/pr-impact.js\"\n\nconst mainCommand = define({\n\tname: \"screenbook\",\n\tdescription: \"Screen catalog and navigation graph generator\",\n\trun: () => {\n\t\tconsole.log(\"Usage: screenbook <command>\")\n\t\tconsole.log(\"\")\n\t\tconsole.log(\"Commands:\")\n\t\tconsole.log(\" init Initialize Screenbook in a project\")\n\t\tconsole.log(\" generate Auto-generate screen.meta.ts from routes\")\n\t\tconsole.log(\" build Build screen metadata JSON\")\n\t\tconsole.log(\" dev Start the development server\")\n\t\tconsole.log(\" lint Detect routes without screen.meta\")\n\t\tconsole.log(\" impact Analyze API dependency impact\")\n\t\tconsole.log(\" pr-impact Analyze PR changes impact\")\n\t\tconsole.log(\" doctor Diagnose common setup issues\")\n\t\tconsole.log(\"\")\n\t\tconsole.log(\"Run 'screenbook <command> --help' for more information\")\n\t},\n})\n\nawait cli(process.argv.slice(2), mainCommand, {\n\tname: \"screenbook\",\n\tversion: \"0.0.1\",\n\tsubCommands: {\n\t\tinit: initCommand,\n\t\tgenerate: generateCommand,\n\t\tbuild: buildCommand,\n\t\tdev: devCommand,\n\t\tlint: lintCommand,\n\t\timpact: impactCommand,\n\t\t\"pr-impact\": prImpactCommand,\n\t\tdoctor: doctorCommand,\n\t},\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["CONFIG_FILES","duplicateIds: string[]","cycles: CycleInfo[]","path: string[]","current: string | null | undefined","lines: string[]","errors: ValidationError[]","bestMatch: string | undefined","matrix: number[][]","lines: string[]","screens: ScreenWithFilePath[]","routeFiles: string[]","missing: CoverageData[\"missing\"]","byOwner: CoverageData[\"byOwner\"]","byTag: CoverageData[\"byTag\"]","lines: string[]","screens: ScreenWithFilePath[]","results: CheckResult[]","version","summary: string[]","result: FlatRoute[]","fullPath: string","warnings: string[]","content: string","ast: ReturnType<typeof parse>","routes: ParsedRoute[]","parseRoutesArray","classNode: any","parseRouteObject","path: string | undefined","component: string | undefined","children: ParsedRoute[] | undefined","redirectTo: string | undefined","loc","warnings: string[]","content: string","ast: ReturnType<typeof parse>","routes: ParsedRoute[]","parseRoutesArray","parseRouteObject","paths: string[]","component: string | undefined","children: ParsedRoute[] | undefined","extractLazyImportPath","loc","warnings: string[]","content: string","ast: ReturnType<typeof parse>","extractLazyImportPath","routeDef: RouteDefinition","parentVarName: string | undefined","childNames: string[]","routes: ParsedRoute[]","route: ParsedRoute","children: ParsedRoute[]","warnings: string[]","content: string","ast: ReturnType<typeof parse>","routes: ParsedRoute[]","parseRoutesArray","parseRouteObject","route: ParsedRoute","parts: string[]","loc","warnings: string[]","content: string","ast: ReturnType<typeof parse>","routes: ParsedRoute[]","route: ParsedRoute","parseResult: ParseResult","screenMeta: InferredScreenMeta","transitive: TransitiveDependency[]","queue: Array<{ id: string; path: string[] }>","lines: string[]","screens: Screen[]","FRAMEWORKS: FrameworkDefinition[]","framework: FrameworkInfo | null","missingMeta: string[]","covered: string[]","flatRoutes: FlatRoute[]","parseResult: ParseResult","missingMeta: FlatRoute[]","covered: FlatRoute[]","invalid: InvalidNavigation[]","orphans: Screen[]","lines: string[]","changedFiles: string[]","screens: Screen[]","results: ImpactResult[]","version: string"],"sources":["../src/utils/config.ts","../src/utils/cycleDetection.ts","../src/utils/errors.ts","../src/utils/logger.ts","../src/utils/validation.ts","../src/commands/build.ts","../src/commands/dev.ts","../src/commands/doctor.ts","../src/utils/routeParserUtils.ts","../src/utils/angularRouterParser.ts","../src/utils/solidRouterParser.ts","../src/utils/tanstackRouterParser.ts","../src/utils/reactRouterParser.ts","../src/utils/vueRouterParser.ts","../src/commands/generate.ts","../src/utils/impactAnalysis.ts","../src/commands/impact.ts","../src/utils/detectFramework.ts","../src/commands/init.ts","../src/commands/lint.ts","../src/utils/prImpact.ts","../src/commands/pr-impact.ts","../src/index.ts"],"sourcesContent":["import { existsSync } from \"node:fs\"\nimport { resolve } from \"node:path\"\nimport { type Config, defineConfig } from \"@screenbook/core\"\nimport { createJiti } from \"jiti\"\n\nconst CONFIG_FILES = [\n\t\"screenbook.config.ts\",\n\t\"screenbook.config.js\",\n\t\"screenbook.config.mjs\",\n]\n\nexport async function loadConfig(configPath?: string): Promise<Config> {\n\tconst cwd = process.cwd()\n\n\t// If config path is provided, use it\n\tif (configPath) {\n\t\tconst absolutePath = resolve(cwd, configPath)\n\t\tif (!existsSync(absolutePath)) {\n\t\t\tthrow new Error(`Config file not found: ${configPath}`)\n\t\t}\n\t\treturn await importConfig(absolutePath, cwd)\n\t}\n\n\t// Search for config file in cwd\n\tfor (const configFile of CONFIG_FILES) {\n\t\tconst absolutePath = resolve(cwd, configFile)\n\t\tif (existsSync(absolutePath)) {\n\t\t\treturn await importConfig(absolutePath, cwd)\n\t\t}\n\t}\n\n\t// Return default config if no config file found\n\treturn defineConfig()\n}\n\nasync function importConfig(\n\tabsolutePath: string,\n\tcwd: string,\n): Promise<Config> {\n\tconst jiti = createJiti(cwd)\n\tconst module = (await jiti.import(absolutePath)) as { default?: Config }\n\n\tif (module.default) {\n\t\treturn module.default\n\t}\n\n\tthrow new Error(`Config file must have a default export: ${absolutePath}`)\n}\n","import type { Screen } from \"@screenbook/core\"\n\n/**\n * Information about a detected cycle\n */\nexport interface CycleInfo {\n\t/**\n\t * Array of screen IDs forming the cycle, including the repeated start node\n\t * @example [\"A\", \"B\", \"C\", \"A\"]\n\t */\n\tcycle: string[]\n\t/**\n\t * True if any screen in the cycle has allowCycles: true\n\t */\n\tallowed: boolean\n}\n\n/**\n * Result of cycle detection analysis\n */\nexport interface CycleDetectionResult {\n\t/**\n\t * True if any cycles were detected\n\t */\n\thasCycles: boolean\n\t/**\n\t * All detected cycles\n\t */\n\tcycles: CycleInfo[]\n\t/**\n\t * Cycles that are not allowed (allowed: false)\n\t */\n\tdisallowedCycles: CycleInfo[]\n\t/**\n\t * Screen IDs that appear more than once (indicates data issue)\n\t */\n\tduplicateIds: string[]\n}\n\n// Node colors for DFS\nenum Color {\n\tWhite = 0, // Unvisited\n\tGray = 1, // In progress (on current path)\n\tBlack = 2, // Completed\n}\n\n/**\n * Detect circular navigation dependencies in screen definitions.\n * Uses DFS with coloring algorithm: O(V + E) complexity.\n *\n * @example\n * ```ts\n * const screens = [\n * { id: \"A\", next: [\"B\"] },\n * { id: \"B\", next: [\"C\"] },\n * { id: \"C\", next: [\"A\"] }, // Creates cycle A → B → C → A\n * ]\n * const result = detectCycles(screens)\n * // result.hasCycles === true\n * // result.cycles[0].cycle === [\"A\", \"B\", \"C\", \"A\"]\n * ```\n */\nexport function detectCycles(screens: Screen[]): CycleDetectionResult {\n\tconst screenMap = new Map<string, Screen>()\n\tconst duplicateIds: string[] = []\n\n\t// Build screen map and detect duplicates\n\tfor (const screen of screens) {\n\t\tif (!screen.id || typeof screen.id !== \"string\") {\n\t\t\t// Skip screens with invalid IDs\n\t\t\tcontinue\n\t\t}\n\t\tif (screenMap.has(screen.id)) {\n\t\t\tduplicateIds.push(screen.id)\n\t\t}\n\t\tscreenMap.set(screen.id, screen)\n\t}\n\n\tconst color = new Map<string, Color>()\n\tconst parent = new Map<string, string | null>()\n\tconst cycles: CycleInfo[] = []\n\n\t// Initialize all nodes as white\n\tfor (const id of screenMap.keys()) {\n\t\tcolor.set(id, Color.White)\n\t}\n\n\t// DFS from each unvisited node\n\tfor (const id of screenMap.keys()) {\n\t\tif (color.get(id) === Color.White) {\n\t\t\tdfs(id, null)\n\t\t}\n\t}\n\n\tfunction dfs(nodeId: string, parentId: string | null): void {\n\t\tcolor.set(nodeId, Color.Gray)\n\t\tparent.set(nodeId, parentId)\n\n\t\tconst node = screenMap.get(nodeId)\n\t\tconst neighbors = node?.next ?? []\n\n\t\tfor (const neighborId of neighbors) {\n\t\t\tconst neighborColor = color.get(neighborId)\n\n\t\t\tif (neighborColor === Color.Gray) {\n\t\t\t\t// Back edge found - cycle detected\n\t\t\t\tconst cyclePath = reconstructCycle(nodeId, neighborId)\n\t\t\t\tconst allowed = isCycleAllowed(cyclePath, screenMap)\n\t\t\t\tcycles.push({ cycle: cyclePath, allowed })\n\t\t\t} else if (neighborColor === Color.White) {\n\t\t\t\t// Only visit if the neighbor exists in our screen map\n\t\t\t\tif (screenMap.has(neighborId)) {\n\t\t\t\t\tdfs(neighborId, nodeId)\n\t\t\t\t}\n\t\t\t}\n\t\t\t// If Black, already fully processed - skip\n\t\t}\n\n\t\tcolor.set(nodeId, Color.Black)\n\t}\n\n\t/**\n\t * Reconstruct cycle path from back edge\n\t */\n\tfunction reconstructCycle(from: string, to: string): string[] {\n\t\tconst path: string[] = []\n\t\tlet current: string | null | undefined = from\n\t\tconst visited = new Set<string>()\n\t\tconst maxIterations = screenMap.size + 1\n\n\t\t// Trace back from 'from' to 'to' with loop guard\n\t\twhile (\n\t\t\tcurrent &&\n\t\t\tcurrent !== to &&\n\t\t\t!visited.has(current) &&\n\t\t\tpath.length < maxIterations\n\t\t) {\n\t\t\tvisited.add(current)\n\t\t\tpath.unshift(current)\n\t\t\tcurrent = parent.get(current)\n\t\t}\n\n\t\t// Add 'to' at the beginning (the cycle start)\n\t\tpath.unshift(to)\n\n\t\t// Add 'to' at the end to close the cycle\n\t\tpath.push(to)\n\n\t\treturn path\n\t}\n\n\tconst disallowedCycles = cycles.filter((c) => !c.allowed)\n\n\treturn {\n\t\thasCycles: cycles.length > 0,\n\t\tcycles,\n\t\tdisallowedCycles,\n\t\tduplicateIds,\n\t}\n}\n\n/**\n * Check if any screen in the cycle has allowCycles: true\n */\nfunction isCycleAllowed(\n\tcyclePath: string[],\n\tscreenMap: Map<string, Screen>,\n): boolean {\n\t// Exclude the last element as it's a duplicate of the first\n\tconst uniqueNodes = cyclePath.slice(0, -1)\n\n\tfor (const nodeId of uniqueNodes) {\n\t\tconst screen = screenMap.get(nodeId)\n\t\tif (screen?.allowCycles === true) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n/**\n * Format cycle information for console output\n *\n * @example\n * ```\n * Cycle 1: A → B → C → A\n * Cycle 2 (allowed): D → E → D\n * ```\n */\nexport function formatCycleWarnings(cycles: CycleInfo[]): string {\n\tif (cycles.length === 0) {\n\t\treturn \"\"\n\t}\n\n\tconst lines: string[] = []\n\n\tfor (let i = 0; i < cycles.length; i++) {\n\t\tconst cycle = cycles[i]\n\t\tif (!cycle) continue\n\n\t\tconst cycleStr = cycle.cycle.join(\" → \")\n\t\tconst allowedSuffix = cycle.allowed ? \" (allowed)\" : \"\"\n\t\tlines.push(` Cycle ${i + 1}${allowedSuffix}: ${cycleStr}`)\n\t}\n\n\treturn lines.join(\"\\n\")\n}\n\n/**\n * Get a summary of cycle detection results\n */\nexport function getCycleSummary(result: CycleDetectionResult): string {\n\tif (!result.hasCycles) {\n\t\treturn \"No circular navigation detected\"\n\t}\n\n\tconst total = result.cycles.length\n\tconst disallowed = result.disallowedCycles.length\n\tconst allowed = total - disallowed\n\n\tif (disallowed === 0) {\n\t\treturn `${total} circular navigation${total > 1 ? \"s\" : \"\"} detected (all allowed)`\n\t}\n\n\tif (allowed === 0) {\n\t\treturn `${total} circular navigation${total > 1 ? \"s\" : \"\"} detected`\n\t}\n\n\treturn `${total} circular navigation${total > 1 ? \"s\" : \"\"} detected (${disallowed} not allowed, ${allowed} allowed)`\n}\n","import type { ErrorOptions } from \"./logger.js\"\n\n/**\n * Centralized error definitions for consistent error messages across CLI commands\n */\nexport const ERRORS = {\n\t// ============================================\n\t// Configuration Errors\n\t// ============================================\n\n\tROUTES_PATTERN_MISSING: {\n\t\ttitle: \"Routes configuration not found\",\n\t\tsuggestion:\n\t\t\t\"Add routesPattern (for file-based routing) or routesFile (for config-based routing) to your screenbook.config.ts.\",\n\t\texample: `import { defineConfig } from \"@screenbook/core\"\n\n// Option 1: File-based routing (Next.js, Nuxt, etc.)\nexport default defineConfig({\n routesPattern: \"src/pages/**/page.tsx\",\n})\n\n// Option 2: Config-based routing (Vue Router, React Router, etc.)\nexport default defineConfig({\n routesFile: \"src/router/routes.ts\",\n})`,\n\t} satisfies ErrorOptions,\n\n\tROUTES_FILE_NOT_FOUND: (filePath: string): ErrorOptions => ({\n\t\ttitle: `Routes file not found: ${filePath}`,\n\t\tsuggestion:\n\t\t\t\"Check the routesFile path in your screenbook.config.ts. The file should export a routes array.\",\n\t\texample: `import { defineConfig } from \"@screenbook/core\"\n\nexport default defineConfig({\n routesFile: \"src/router/routes.ts\", // Make sure this file exists\n})`,\n\t}),\n\n\tROUTES_FILE_PARSE_ERROR: (filePath: string, error: string): ErrorOptions => ({\n\t\ttitle: `Failed to parse routes file: ${filePath}`,\n\t\tmessage: error,\n\t\tsuggestion:\n\t\t\t\"Ensure the file exports a valid routes array. Check for syntax errors or unsupported patterns.\",\n\t}),\n\n\tCONFIG_NOT_FOUND: {\n\t\ttitle: \"Configuration file not found\",\n\t\tsuggestion:\n\t\t\t\"Run 'screenbook init' to create a screenbook.config.ts file, or create one manually.\",\n\t\texample: `import { defineConfig } from \"@screenbook/core\"\n\nexport default defineConfig({\n metaPattern: \"src/**/screen.meta.ts\",\n})`,\n\t} satisfies ErrorOptions,\n\n\t// ============================================\n\t// Build/File Errors\n\t// ============================================\n\n\tSCREENS_NOT_FOUND: {\n\t\ttitle: \"screens.json not found\",\n\t\tsuggestion: \"Run 'screenbook build' first to generate the screen catalog.\",\n\t\tmessage:\n\t\t\t\"If you haven't set up Screenbook yet, run 'screenbook init' to get started.\",\n\t} satisfies ErrorOptions,\n\n\tSCREENS_PARSE_ERROR: {\n\t\ttitle: \"Failed to parse screens.json\",\n\t\tsuggestion:\n\t\t\t\"The screens.json file may be corrupted. Try running 'screenbook build' to regenerate it.\",\n\t} satisfies ErrorOptions,\n\n\tMETA_FILE_LOAD_ERROR: (filePath: string): ErrorOptions => ({\n\t\ttitle: `Failed to load ${filePath}`,\n\t\tsuggestion:\n\t\t\t\"Check the file for syntax errors or missing exports. The file should export a 'screen' object.\",\n\t\texample: `import { defineScreen } from \"@screenbook/core\"\n\nexport const screen = defineScreen({\n id: \"example.screen\",\n title: \"Example Screen\",\n route: \"/example\",\n})`,\n\t}),\n\n\t// ============================================\n\t// Command Argument Errors\n\t// ============================================\n\n\tAPI_NAME_REQUIRED: {\n\t\ttitle: \"API name is required\",\n\t\tsuggestion: \"Provide the API name as an argument.\",\n\t\texample: `screenbook impact UserAPI.getProfile\nscreenbook impact \"PaymentAPI.*\" # Use quotes for patterns`,\n\t} satisfies ErrorOptions,\n\n\t// ============================================\n\t// Git Errors\n\t// ============================================\n\n\tGIT_CHANGED_FILES_ERROR: (baseBranch: string): ErrorOptions => ({\n\t\ttitle: \"Failed to get changed files from git\",\n\t\tmessage: `Make sure you are in a git repository and the base branch '${baseBranch}' exists.`,\n\t\tsuggestion: `Verify the base branch exists with: git branch -a | grep ${baseBranch}`,\n\t}),\n\n\tGIT_NOT_REPOSITORY: {\n\t\ttitle: \"Not a git repository\",\n\t\tsuggestion:\n\t\t\t\"This command requires a git repository. Initialize one with 'git init' or navigate to an existing repository.\",\n\t} satisfies ErrorOptions,\n\n\t// ============================================\n\t// Server Errors\n\t// ============================================\n\n\tSERVER_START_FAILED: (error: string): ErrorOptions => ({\n\t\ttitle: \"Failed to start development server\",\n\t\tmessage: error,\n\t\tsuggestion:\n\t\t\t\"Check if the port is already in use or if there are any dependency issues.\",\n\t}),\n\n\t// ============================================\n\t// Validation Errors\n\t// ============================================\n\n\tVALIDATION_FAILED: (errorCount: number): ErrorOptions => ({\n\t\ttitle: `Validation failed with ${errorCount} error${errorCount === 1 ? \"\" : \"s\"}`,\n\t\tsuggestion:\n\t\t\t\"Fix the validation errors above. Screen references must point to existing screens.\",\n\t}),\n\n\t// ============================================\n\t// Lint Errors\n\t// ============================================\n\n\tLINT_MISSING_META: (\n\t\tmissingCount: number,\n\t\ttotalRoutes: number,\n\t): ErrorOptions => ({\n\t\ttitle: `${missingCount} route${missingCount === 1 ? \"\" : \"s\"} missing screen.meta.ts`,\n\t\tmessage: `Found ${totalRoutes} route file${totalRoutes === 1 ? \"\" : \"s\"}, but ${missingCount} ${missingCount === 1 ? \"is\" : \"are\"} missing colocated screen.meta.ts.`,\n\t\tsuggestion:\n\t\t\t\"Add screen.meta.ts files next to your route files, or run 'screenbook generate' to create them.\",\n\t}),\n\n\t// ============================================\n\t// Cycle Detection Errors\n\t// ============================================\n\n\tCYCLES_DETECTED: (cycleCount: number): ErrorOptions => ({\n\t\ttitle: `${cycleCount} circular navigation${cycleCount === 1 ? \"\" : \"s\"} detected`,\n\t\tsuggestion:\n\t\t\t\"Review the navigation flow. Use 'allowCycles: true' in screen.meta.ts to allow intentional cycles, or use --allow-cycles to suppress all warnings.\",\n\t\texample: `// Allow a specific screen to be part of cycles\nexport const screen = defineScreen({\n id: \"billing.invoice.detail\",\n next: [\"billing.invoices\"],\n allowCycles: true, // This cycle is intentional\n})`,\n\t}),\n}\n","import pc from \"picocolors\"\n\n/**\n * Structured error options for detailed error messages\n */\nexport interface ErrorOptions {\n\t/** Error title (shown after \"Error:\") */\n\ttitle: string\n\t/** Additional error message/details */\n\tmessage?: string\n\t/** Actionable suggestion for the user */\n\tsuggestion?: string\n\t/** Code example to help the user */\n\texample?: string\n}\n\n/**\n * Logger utility for consistent, color-coded CLI output\n */\nexport const logger = {\n\t// ============================================\n\t// Status Messages\n\t// ============================================\n\n\t/**\n\t * Success message (green checkmark)\n\t */\n\tsuccess: (msg: string): void => {\n\t\tconsole.log(`${pc.green(\"✓\")} ${msg}`)\n\t},\n\n\t/**\n\t * Error message (red X)\n\t */\n\terror: (msg: string): void => {\n\t\tconsole.error(`${pc.red(\"✗\")} ${pc.red(`Error: ${msg}`)}`)\n\t},\n\n\t/**\n\t * Warning message (yellow warning sign)\n\t */\n\twarn: (msg: string): void => {\n\t\tconsole.log(`${pc.yellow(\"⚠\")} ${pc.yellow(`Warning: ${msg}`)}`)\n\t},\n\n\t/**\n\t * Info message (cyan info icon)\n\t */\n\tinfo: (msg: string): void => {\n\t\tconsole.log(`${pc.cyan(\"ℹ\")} ${msg}`)\n\t},\n\n\t// ============================================\n\t// Detailed Error with Guidance\n\t// ============================================\n\n\t/**\n\t * Display a detailed error with actionable suggestions\n\t */\n\terrorWithHelp: (options: ErrorOptions): void => {\n\t\tconst { title, message, suggestion, example } = options\n\n\t\tconsole.error()\n\t\tconsole.error(`${pc.red(\"✗\")} ${pc.red(`Error: ${title}`)}`)\n\n\t\tif (message) {\n\t\t\tconsole.error()\n\t\t\tconsole.error(` ${message}`)\n\t\t}\n\n\t\tif (suggestion) {\n\t\t\tconsole.error()\n\t\t\tconsole.error(` ${pc.cyan(\"Suggestion:\")} ${suggestion}`)\n\t\t}\n\n\t\tif (example) {\n\t\t\tconsole.error()\n\t\t\tconsole.error(` ${pc.dim(\"Example:\")}`)\n\t\t\tfor (const line of example.split(\"\\n\")) {\n\t\t\t\tconsole.error(` ${pc.dim(line)}`)\n\t\t\t}\n\t\t}\n\n\t\tconsole.error()\n\t},\n\n\t// ============================================\n\t// Progress Indicators\n\t// ============================================\n\n\t/**\n\t * Step indicator (dimmed arrow)\n\t */\n\tstep: (msg: string): void => {\n\t\tconsole.log(`${pc.dim(\"→\")} ${msg}`)\n\t},\n\n\t/**\n\t * Done/completed indicator (green checkmark with green text)\n\t */\n\tdone: (msg: string): void => {\n\t\tconsole.log(`${pc.green(\"✓\")} ${pc.green(msg)}`)\n\t},\n\n\t/**\n\t * Item success (green checkmark, indented)\n\t */\n\titemSuccess: (msg: string): void => {\n\t\tconsole.log(` ${pc.green(\"✓\")} ${msg}`)\n\t},\n\n\t/**\n\t * Item failure (red X, indented)\n\t */\n\titemError: (msg: string): void => {\n\t\tconsole.log(` ${pc.red(\"✗\")} ${msg}`)\n\t},\n\n\t/**\n\t * Item warning (yellow warning, indented)\n\t */\n\titemWarn: (msg: string): void => {\n\t\tconsole.log(` ${pc.yellow(\"⚠\")} ${msg}`)\n\t},\n\n\t// ============================================\n\t// Plain Output\n\t// ============================================\n\n\t/**\n\t * Plain log (no prefix)\n\t */\n\tlog: (msg: string): void => {\n\t\tconsole.log(msg)\n\t},\n\n\t/**\n\t * Blank line\n\t */\n\tblank: (): void => {\n\t\tconsole.log()\n\t},\n\n\t// ============================================\n\t// Formatting Helpers\n\t// ============================================\n\n\t/**\n\t * Bold text\n\t */\n\tbold: (msg: string): string => pc.bold(msg),\n\n\t/**\n\t * Dimmed text\n\t */\n\tdim: (msg: string): string => pc.dim(msg),\n\n\t/**\n\t * Code/command text (cyan)\n\t */\n\tcode: (msg: string): string => pc.cyan(msg),\n\n\t/**\n\t * File path text (underlined)\n\t */\n\tpath: (msg: string): string => pc.underline(msg),\n\n\t/**\n\t * Highlight text (cyan bold)\n\t */\n\thighlight: (msg: string): string => pc.cyan(pc.bold(msg)),\n\n\t/**\n\t * Green text\n\t */\n\tgreen: (msg: string): string => pc.green(msg),\n\n\t/**\n\t * Red text\n\t */\n\tred: (msg: string): string => pc.red(msg),\n\n\t/**\n\t * Yellow text\n\t */\n\tyellow: (msg: string): string => pc.yellow(msg),\n}\n","import type { Screen } from \"@screenbook/core\"\n\nexport interface ValidationError {\n\tscreenId: string\n\tfield: \"next\" | \"entryPoints\"\n\tinvalidRef: string\n\tsuggestion?: string\n}\n\nexport interface ValidationResult {\n\tvalid: boolean\n\terrors: ValidationError[]\n}\n\n/**\n * Validate screen references (next and entryPoints)\n */\nexport function validateScreenReferences(screens: Screen[]): ValidationResult {\n\tconst screenIds = new Set(screens.map((s) => s.id))\n\tconst errors: ValidationError[] = []\n\n\tfor (const screen of screens) {\n\t\t// Validate next references\n\t\tif (screen.next) {\n\t\t\tfor (const nextId of screen.next) {\n\t\t\t\tif (!screenIds.has(nextId)) {\n\t\t\t\t\terrors.push({\n\t\t\t\t\t\tscreenId: screen.id,\n\t\t\t\t\t\tfield: \"next\",\n\t\t\t\t\t\tinvalidRef: nextId,\n\t\t\t\t\t\tsuggestion: findSimilar(nextId, screenIds),\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Validate entryPoints references\n\t\tif (screen.entryPoints) {\n\t\t\tfor (const entryId of screen.entryPoints) {\n\t\t\t\tif (!screenIds.has(entryId)) {\n\t\t\t\t\terrors.push({\n\t\t\t\t\t\tscreenId: screen.id,\n\t\t\t\t\t\tfield: \"entryPoints\",\n\t\t\t\t\t\tinvalidRef: entryId,\n\t\t\t\t\t\tsuggestion: findSimilar(entryId, screenIds),\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\tvalid: errors.length === 0,\n\t\terrors,\n\t}\n}\n\n/**\n * Find similar screen ID using Levenshtein distance\n */\nfunction findSimilar(\n\ttarget: string,\n\tcandidates: Set<string>,\n): string | undefined {\n\tlet bestMatch: string | undefined\n\tlet bestDistance = Number.POSITIVE_INFINITY\n\n\t// Only suggest if distance is reasonable (less than 40% of target length)\n\tconst maxDistance = Math.ceil(target.length * 0.4)\n\n\tfor (const candidate of candidates) {\n\t\tconst distance = levenshteinDistance(target, candidate)\n\t\tif (distance < bestDistance && distance <= maxDistance) {\n\t\t\tbestDistance = distance\n\t\t\tbestMatch = candidate\n\t\t}\n\t}\n\n\treturn bestMatch\n}\n\n/**\n * Calculate Levenshtein distance between two strings\n */\nfunction levenshteinDistance(a: string, b: string): number {\n\t// Pre-initialize matrix with proper dimensions\n\tconst matrix: number[][] = Array.from({ length: a.length + 1 }, () =>\n\t\tArray.from({ length: b.length + 1 }, () => 0),\n\t)\n\n\t// Helper to safely get/set matrix values (matrix is pre-initialized, so these are always valid)\n\tconst get = (i: number, j: number): number => matrix[i]?.[j] ?? 0\n\tconst set = (i: number, j: number, value: number): void => {\n\t\tconst row = matrix[i]\n\t\tif (row) row[j] = value\n\t}\n\n\t// Initialize first column\n\tfor (let i = 0; i <= a.length; i++) {\n\t\tset(i, 0, i)\n\t}\n\n\t// Initialize first row\n\tfor (let j = 0; j <= b.length; j++) {\n\t\tset(0, j, j)\n\t}\n\n\t// Fill the matrix\n\tfor (let i = 1; i <= a.length; i++) {\n\t\tfor (let j = 1; j <= b.length; j++) {\n\t\t\tconst cost = a[i - 1] === b[j - 1] ? 0 : 1\n\t\t\tset(\n\t\t\t\ti,\n\t\t\t\tj,\n\t\t\t\tMath.min(\n\t\t\t\t\tget(i - 1, j) + 1, // deletion\n\t\t\t\t\tget(i, j - 1) + 1, // insertion\n\t\t\t\t\tget(i - 1, j - 1) + cost, // substitution\n\t\t\t\t),\n\t\t\t)\n\t\t}\n\t}\n\n\treturn get(a.length, b.length)\n}\n\n/**\n * Format validation errors for console output\n */\nexport function formatValidationErrors(errors: ValidationError[]): string {\n\tconst lines: string[] = []\n\n\tfor (const error of errors) {\n\t\tlines.push(` Screen \"${error.screenId}\"`)\n\t\tlines.push(\n\t\t\t` → ${error.field} references non-existent screen \"${error.invalidRef}\"`,\n\t\t)\n\t\tif (error.suggestion) {\n\t\t\tlines.push(` Did you mean \"${error.suggestion}\"?`)\n\t\t}\n\t\tlines.push(\"\")\n\t}\n\n\treturn lines.join(\"\\n\")\n}\n","import { existsSync, mkdirSync, writeFileSync } from \"node:fs\"\nimport { dirname, join, resolve } from \"node:path\"\nimport type { Screen } from \"@screenbook/core\"\nimport { define } from \"gunshi\"\nimport { createJiti } from \"jiti\"\nimport { glob } from \"tinyglobby\"\nimport { loadConfig } from \"../utils/config.js\"\nimport {\n\tdetectCycles,\n\tformatCycleWarnings,\n\tgetCycleSummary,\n} from \"../utils/cycleDetection.js\"\nimport { ERRORS } from \"../utils/errors.js\"\nimport { logger } from \"../utils/logger.js\"\nimport {\n\tformatValidationErrors,\n\tvalidateScreenReferences,\n} from \"../utils/validation.js\"\n\nexport interface CoverageData {\n\ttotal: number\n\tcovered: number\n\tpercentage: number\n\tmissing: Array<{\n\t\troute: string\n\t\tsuggestedPath: string\n\t}>\n\tbyOwner: Record<string, { count: number; screens: string[] }>\n\tbyTag: Record<string, number>\n\ttimestamp: string\n}\n\nexport const buildCommand = define({\n\tname: \"build\",\n\tdescription: \"Build screen metadata JSON from screen.meta.ts files\",\n\targs: {\n\t\tconfig: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"c\",\n\t\t\tdescription: \"Path to config file\",\n\t\t},\n\t\toutDir: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"o\",\n\t\t\tdescription: \"Output directory\",\n\t\t},\n\t\tstrict: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"s\",\n\t\t\tdescription: \"Fail on validation errors and disallowed cycles\",\n\t\t\tdefault: false,\n\t\t},\n\t\tallowCycles: {\n\t\t\ttype: \"boolean\",\n\t\t\tdescription: \"Suppress all circular navigation warnings\",\n\t\t\tdefault: false,\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tconst config = await loadConfig(ctx.values.config)\n\t\tconst outDir = ctx.values.outDir ?? config.outDir\n\t\tconst cwd = process.cwd()\n\n\t\tlogger.info(\"Building screen metadata...\")\n\n\t\t// Find all screen.meta.ts files\n\t\tconst files = await glob(config.metaPattern, {\n\t\t\tcwd,\n\t\t\tignore: config.ignore,\n\t\t})\n\n\t\tif (files.length === 0) {\n\t\t\tlogger.warn(\n\t\t\t\t`No screen.meta.ts files found matching: ${config.metaPattern}`,\n\t\t\t)\n\t\t\treturn\n\t\t}\n\n\t\tlogger.info(`Found ${files.length} screen files`)\n\n\t\t// Create jiti instance for loading TypeScript files\n\t\tconst jiti = createJiti(cwd)\n\n\t\t// Extended screen type with file path (for internal use)\n\t\ttype ScreenWithFilePath = Screen & { filePath: string }\n\n\t\t// Load and collect screen metadata\n\t\tconst screens: ScreenWithFilePath[] = []\n\n\t\tfor (const file of files) {\n\t\t\tconst absolutePath = resolve(cwd, file)\n\n\t\t\ttry {\n\t\t\t\tconst module = (await jiti.import(absolutePath)) as { screen?: Screen }\n\t\t\t\tif (module.screen) {\n\t\t\t\t\tscreens.push({ ...module.screen, filePath: absolutePath })\n\t\t\t\t\tlogger.itemSuccess(module.screen.id)\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tlogger.itemError(`Failed to load ${file}`)\n\t\t\t\tif (error instanceof Error) {\n\t\t\t\t\tlogger.log(` ${logger.dim(error.message)}`)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Validate screen references\n\t\tconst validation = validateScreenReferences(screens)\n\t\tif (!validation.valid) {\n\t\t\tlogger.blank()\n\t\t\tlogger.warn(\"Invalid screen references found:\")\n\t\t\tlogger.log(formatValidationErrors(validation.errors))\n\n\t\t\tif (ctx.values.strict) {\n\t\t\t\tlogger.errorWithHelp(ERRORS.VALIDATION_FAILED(validation.errors.length))\n\t\t\t\tprocess.exit(1)\n\t\t\t}\n\t\t}\n\n\t\t// Detect circular navigation\n\t\tif (!ctx.values.allowCycles) {\n\t\t\tconst cycleResult = detectCycles(screens)\n\t\t\tif (cycleResult.hasCycles) {\n\t\t\t\tlogger.blank()\n\t\t\t\tlogger.warn(getCycleSummary(cycleResult))\n\t\t\t\tlogger.log(formatCycleWarnings(cycleResult.cycles))\n\n\t\t\t\tif (ctx.values.strict && cycleResult.disallowedCycles.length > 0) {\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tlogger.errorWithHelp(\n\t\t\t\t\t\tERRORS.CYCLES_DETECTED(cycleResult.disallowedCycles.length),\n\t\t\t\t\t)\n\t\t\t\t\tprocess.exit(1)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Generate screens.json\n\t\tconst outputPath = join(cwd, outDir, \"screens.json\")\n\t\tconst outputDir = dirname(outputPath)\n\n\t\tif (!existsSync(outputDir)) {\n\t\t\tmkdirSync(outputDir, { recursive: true })\n\t\t}\n\n\t\twriteFileSync(outputPath, JSON.stringify(screens, null, 2))\n\t\tlogger.blank()\n\t\tlogger.success(`Generated ${logger.path(outputPath)}`)\n\n\t\t// Generate Mermaid graph\n\t\tconst mermaidPath = join(cwd, outDir, \"graph.mmd\")\n\t\tconst mermaidContent = generateMermaidGraph(screens)\n\t\twriteFileSync(mermaidPath, mermaidContent)\n\t\tlogger.success(`Generated ${logger.path(mermaidPath)}`)\n\n\t\t// Generate coverage.json\n\t\tconst coverage = await generateCoverageData(config, cwd, screens)\n\t\tconst coveragePath = join(cwd, outDir, \"coverage.json\")\n\t\twriteFileSync(coveragePath, JSON.stringify(coverage, null, 2))\n\t\tlogger.success(`Generated ${logger.path(coveragePath)}`)\n\t\tlogger.blank()\n\t\tlogger.done(\n\t\t\t`Coverage: ${coverage.covered}/${coverage.total} (${coverage.percentage}%)`,\n\t\t)\n\t},\n})\n\nasync function generateCoverageData(\n\tconfig: { routesPattern?: string; metaPattern: string; ignore: string[] },\n\tcwd: string,\n\tscreens: Screen[],\n): Promise<CoverageData> {\n\t// Get all route files if routesPattern is configured\n\tlet routeFiles: string[] = []\n\tif (config.routesPattern) {\n\t\trouteFiles = await glob(config.routesPattern, {\n\t\t\tcwd,\n\t\t\tignore: config.ignore,\n\t\t})\n\t}\n\n\t// Get directories that have screen.meta.ts\n\tconst _metaDirs = new Set(\n\t\tscreens.map((s) => {\n\t\t\t// Extract directory from route or id\n\t\t\tconst parts = s.id.split(\".\")\n\t\t\treturn parts.slice(0, -1).join(\"/\") || parts[0]\n\t\t}),\n\t)\n\n\t// Find missing routes (routes without screen.meta.ts)\n\tconst missing: CoverageData[\"missing\"] = []\n\tfor (const routeFile of routeFiles) {\n\t\tconst routeDir = dirname(routeFile)\n\t\tconst hasMetaFile = screens.some((s) => {\n\t\t\t// Check if any screen's route matches this route file's directory\n\t\t\tconst screenDir = s.id.replace(/\\./g, \"/\")\n\t\t\treturn (\n\t\t\t\trouteDir.includes(screenDir) ||\n\t\t\t\tscreenDir.includes(\n\t\t\t\t\trouteDir.replace(/^src\\/pages\\//, \"\").replace(/^app\\//, \"\"),\n\t\t\t\t)\n\t\t\t)\n\t\t})\n\n\t\tif (!hasMetaFile) {\n\t\t\tmissing.push({\n\t\t\t\troute: routeFile,\n\t\t\t\tsuggestedPath: join(dirname(routeFile), \"screen.meta.ts\"),\n\t\t\t})\n\t\t}\n\t}\n\n\t// Calculate coverage\n\tconst total = routeFiles.length > 0 ? routeFiles.length : screens.length\n\tconst covered = screens.length\n\tconst percentage = total > 0 ? Math.round((covered / total) * 100) : 100\n\n\t// Group by owner\n\tconst byOwner: CoverageData[\"byOwner\"] = {}\n\tfor (const screen of screens) {\n\t\tconst owners = screen.owner || [\"unassigned\"]\n\t\tfor (const owner of owners) {\n\t\t\tif (!byOwner[owner]) {\n\t\t\t\tbyOwner[owner] = { count: 0, screens: [] }\n\t\t\t}\n\t\t\tbyOwner[owner].count++\n\t\t\tbyOwner[owner].screens.push(screen.id)\n\t\t}\n\t}\n\n\t// Group by tag\n\tconst byTag: CoverageData[\"byTag\"] = {}\n\tfor (const screen of screens) {\n\t\tconst tags = screen.tags || []\n\t\tfor (const tag of tags) {\n\t\t\tbyTag[tag] = (byTag[tag] || 0) + 1\n\t\t}\n\t}\n\n\treturn {\n\t\ttotal,\n\t\tcovered,\n\t\tpercentage,\n\t\tmissing,\n\t\tbyOwner,\n\t\tbyTag,\n\t\ttimestamp: new Date().toISOString(),\n\t}\n}\n\nfunction generateMermaidGraph(screens: Screen[]): string {\n\tconst lines: string[] = [\"flowchart TD\"]\n\n\t// Create nodes\n\tfor (const screen of screens) {\n\t\tconst label = screen.title.replace(/\"/g, \"'\")\n\t\tlines.push(` ${sanitizeId(screen.id)}[\"${label}\"]`)\n\t}\n\n\tlines.push(\"\")\n\n\t// Create edges from next relationships\n\tfor (const screen of screens) {\n\t\tif (screen.next) {\n\t\t\tfor (const nextId of screen.next) {\n\t\t\t\tlines.push(` ${sanitizeId(screen.id)} --> ${sanitizeId(nextId)}`)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn lines.join(\"\\n\")\n}\n\nfunction sanitizeId(id: string): string {\n\treturn id.replace(/\\./g, \"_\")\n}\n","import { spawn } from \"node:child_process\"\nimport { copyFileSync, existsSync, mkdirSync, writeFileSync } from \"node:fs\"\nimport { createRequire } from \"node:module\"\nimport { dirname, join, resolve } from \"node:path\"\nimport type { Screen } from \"@screenbook/core\"\nimport { define } from \"gunshi\"\nimport { createJiti } from \"jiti\"\nimport { glob } from \"tinyglobby\"\nimport { loadConfig } from \"../utils/config.js\"\nimport { ERRORS } from \"../utils/errors.js\"\nimport { logger } from \"../utils/logger.js\"\n\nexport const devCommand = define({\n\tname: \"dev\",\n\tdescription: \"Start the Screenbook development server\",\n\targs: {\n\t\tconfig: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"c\",\n\t\t\tdescription: \"Path to config file\",\n\t\t},\n\t\tport: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"p\",\n\t\t\tdescription: \"Port to run the server on\",\n\t\t\tdefault: \"4321\",\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tconst config = await loadConfig(ctx.values.config)\n\t\tconst port = ctx.values.port ?? \"4321\"\n\t\tconst cwd = process.cwd()\n\n\t\tlogger.info(\"Starting Screenbook development server...\")\n\n\t\t// First, build the screen metadata\n\t\tawait buildScreens(config, cwd)\n\n\t\t// Find the UI package location\n\t\tconst uiPackagePath = resolveUiPackage()\n\n\t\tif (!uiPackagePath) {\n\t\t\tlogger.errorWithHelp({\n\t\t\t\ttitle: \"Could not find @screenbook/ui package\",\n\t\t\t\tsuggestion:\n\t\t\t\t\t\"Make sure @screenbook/ui is installed. Run 'npm install @screenbook/ui' or 'pnpm add @screenbook/ui'.\",\n\t\t\t})\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\t// Copy screens.json and coverage.json to UI package\n\t\tconst screensJsonPath = join(cwd, config.outDir, \"screens.json\")\n\t\tconst coverageJsonPath = join(cwd, config.outDir, \"coverage.json\")\n\t\tconst uiScreensDir = join(uiPackagePath, \".screenbook\")\n\n\t\tif (!existsSync(uiScreensDir)) {\n\t\t\tmkdirSync(uiScreensDir, { recursive: true })\n\t\t}\n\n\t\tif (existsSync(screensJsonPath)) {\n\t\t\tcopyFileSync(screensJsonPath, join(uiScreensDir, \"screens.json\"))\n\t\t}\n\n\t\tif (existsSync(coverageJsonPath)) {\n\t\t\tcopyFileSync(coverageJsonPath, join(uiScreensDir, \"coverage.json\"))\n\t\t}\n\n\t\t// Start Astro dev server\n\t\tlogger.blank()\n\t\tlogger.info(\n\t\t\t`Starting UI server on ${logger.highlight(`http://localhost:${port}`)}`,\n\t\t)\n\n\t\tconst astroProcess = spawn(\"npx\", [\"astro\", \"dev\", \"--port\", port], {\n\t\t\tcwd: uiPackagePath,\n\t\t\tstdio: \"inherit\",\n\t\t\tshell: true,\n\t\t})\n\n\t\tastroProcess.on(\"error\", (error) => {\n\t\t\tlogger.errorWithHelp(ERRORS.SERVER_START_FAILED(error.message))\n\t\t\tprocess.exit(1)\n\t\t})\n\n\t\tastroProcess.on(\"close\", (code) => {\n\t\t\tprocess.exit(code ?? 0)\n\t\t})\n\n\t\t// Handle graceful shutdown\n\t\tprocess.on(\"SIGINT\", () => {\n\t\t\tastroProcess.kill(\"SIGINT\")\n\t\t})\n\n\t\tprocess.on(\"SIGTERM\", () => {\n\t\t\tastroProcess.kill(\"SIGTERM\")\n\t\t})\n\t},\n})\n\nasync function buildScreens(\n\tconfig: { metaPattern: string; outDir: string; ignore: string[] },\n\tcwd: string,\n): Promise<void> {\n\tconst files = await glob(config.metaPattern, {\n\t\tcwd,\n\t\tignore: config.ignore,\n\t})\n\n\tif (files.length === 0) {\n\t\tlogger.warn(`No screen.meta.ts files found matching: ${config.metaPattern}`)\n\t\treturn\n\t}\n\n\tlogger.info(`Found ${files.length} screen files`)\n\n\t// Extended screen type with file path (for internal use)\n\ttype ScreenWithFilePath = Screen & { filePath: string }\n\n\tconst jiti = createJiti(cwd)\n\tconst screens: ScreenWithFilePath[] = []\n\n\tfor (const file of files) {\n\t\tconst absolutePath = resolve(cwd, file)\n\n\t\ttry {\n\t\t\tconst module = (await jiti.import(absolutePath)) as { screen?: Screen }\n\t\t\tif (module.screen) {\n\t\t\t\tscreens.push({ ...module.screen, filePath: absolutePath })\n\t\t\t\tlogger.itemSuccess(module.screen.id)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tlogger.itemError(`Failed to load ${file}`)\n\t\t\tif (error instanceof Error) {\n\t\t\t\tlogger.log(` ${logger.dim(error.message)}`)\n\t\t\t}\n\t\t}\n\t}\n\n\tconst outputPath = join(cwd, config.outDir, \"screens.json\")\n\tconst outputDir = dirname(outputPath)\n\n\tif (!existsSync(outputDir)) {\n\t\tmkdirSync(outputDir, { recursive: true })\n\t}\n\n\twriteFileSync(outputPath, JSON.stringify(screens, null, 2))\n\tlogger.blank()\n\tlogger.success(`Generated ${logger.path(outputPath)}`)\n}\n\nfunction resolveUiPackage(): string | null {\n\t// Try to resolve @screenbook/ui from node_modules\n\ttry {\n\t\tconst require = createRequire(import.meta.url)\n\t\tconst uiPackageJson = require.resolve(\"@screenbook/ui/package.json\")\n\t\treturn dirname(uiPackageJson)\n\t} catch {\n\t\t// Fallback: look in common locations\n\t\tconst possiblePaths = [\n\t\t\tjoin(process.cwd(), \"node_modules\", \"@screenbook\", \"ui\"),\n\t\t\tjoin(process.cwd(), \"..\", \"ui\"),\n\t\t\tjoin(process.cwd(), \"packages\", \"ui\"),\n\t\t]\n\n\t\tfor (const p of possiblePaths) {\n\t\t\tif (existsSync(join(p, \"package.json\"))) {\n\t\t\t\treturn p\n\t\t\t}\n\t\t}\n\n\t\treturn null\n\t}\n}\n","import { existsSync, readFileSync } from \"node:fs\"\nimport { join, resolve } from \"node:path\"\nimport { define } from \"gunshi\"\nimport { glob } from \"tinyglobby\"\nimport { loadConfig } from \"../utils/config.js\"\nimport { logger } from \"../utils/logger.js\"\n\nconst CONFIG_FILES = [\n\t\"screenbook.config.ts\",\n\t\"screenbook.config.js\",\n\t\"screenbook.config.mjs\",\n]\n\nexport interface CheckResult {\n\tname: string\n\tstatus: \"pass\" | \"fail\" | \"warn\"\n\tmessage: string\n\tsuggestion?: string\n}\n\ninterface PackageJson {\n\tdependencies?: Record<string, string>\n\tdevDependencies?: Record<string, string>\n}\n\nexport const doctorCommand = define({\n\tname: \"doctor\",\n\tdescription: \"Diagnose common issues with Screenbook setup\",\n\targs: {\n\t\tverbose: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"v\",\n\t\t\tdescription: \"Show detailed output\",\n\t\t\tdefault: false,\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tconst cwd = process.cwd()\n\t\tconst verbose = ctx.values.verbose\n\n\t\tlogger.log(\"\")\n\t\tlogger.log(logger.bold(\"Screenbook Doctor\"))\n\t\tlogger.log(\"─────────────────\")\n\t\tlogger.log(\"\")\n\n\t\tconst results: CheckResult[] = []\n\n\t\t// Run all checks\n\t\tresults.push(await checkConfigFile(cwd))\n\t\tresults.push(await checkDependencies(cwd))\n\n\t\t// Load config for remaining checks\n\t\tconst config = await loadConfig()\n\n\t\tresults.push(await checkMetaPattern(cwd, config.metaPattern, config.ignore))\n\t\tresults.push(\n\t\t\tawait checkRoutesPattern(cwd, config.routesPattern, config.ignore),\n\t\t)\n\t\tresults.push(await checkBuildOutput(cwd, config.outDir))\n\t\tresults.push(await checkVersionCompatibility(cwd))\n\t\tresults.push(await checkGitRepository(cwd))\n\n\t\t// Display results\n\t\tdisplayResults(results, verbose)\n\t},\n})\n\n// Exported for testing\nexport async function checkConfigFile(cwd: string): Promise<CheckResult> {\n\tfor (const configFile of CONFIG_FILES) {\n\t\tconst absolutePath = resolve(cwd, configFile)\n\t\tif (existsSync(absolutePath)) {\n\t\t\treturn {\n\t\t\t\tname: \"Config file\",\n\t\t\t\tstatus: \"pass\",\n\t\t\t\tmessage: `Found: ${configFile}`,\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\tname: \"Config file\",\n\t\tstatus: \"warn\",\n\t\tmessage: \"No config file found (using defaults)\",\n\t\tsuggestion: \"Run 'screenbook init' to create a config file\",\n\t}\n}\n\nexport async function checkDependencies(cwd: string): Promise<CheckResult> {\n\tconst packageJsonPath = join(cwd, \"package.json\")\n\n\tif (!existsSync(packageJsonPath)) {\n\t\treturn {\n\t\t\tname: \"Dependencies\",\n\t\t\tstatus: \"fail\",\n\t\t\tmessage: \"package.json not found\",\n\t\t\tsuggestion: \"Run 'npm init' or 'pnpm init' to create package.json\",\n\t\t}\n\t}\n\n\ttry {\n\t\tconst content = readFileSync(packageJsonPath, \"utf-8\")\n\t\tconst pkg = JSON.parse(content) as PackageJson\n\n\t\tconst allDeps = { ...pkg.dependencies, ...pkg.devDependencies }\n\t\tconst unifiedVersion = allDeps.screenbook\n\t\tconst coreVersion = allDeps[\"@screenbook/core\"]\n\t\tconst cliVersion = allDeps[\"@screenbook/cli\"]\n\n\t\t// Check for unified screenbook package first\n\t\tif (unifiedVersion) {\n\t\t\treturn {\n\t\t\t\tname: \"Dependencies\",\n\t\t\t\tstatus: \"pass\",\n\t\t\t\tmessage: `screenbook@${unifiedVersion}`,\n\t\t\t}\n\t\t}\n\n\t\tif (!coreVersion && !cliVersion) {\n\t\t\treturn {\n\t\t\t\tname: \"Dependencies\",\n\t\t\t\tstatus: \"fail\",\n\t\t\t\tmessage: \"Screenbook packages not installed\",\n\t\t\t\tsuggestion:\n\t\t\t\t\t\"Run 'pnpm add -D screenbook' or 'pnpm add -D @screenbook/core @screenbook/cli' to install\",\n\t\t\t}\n\t\t}\n\n\t\tif (!coreVersion) {\n\t\t\treturn {\n\t\t\t\tname: \"Dependencies\",\n\t\t\t\tstatus: \"warn\",\n\t\t\t\tmessage: \"@screenbook/core not found in dependencies\",\n\t\t\t\tsuggestion: \"Run 'pnpm add -D @screenbook/core' to install\",\n\t\t\t}\n\t\t}\n\n\t\tif (!cliVersion) {\n\t\t\treturn {\n\t\t\t\tname: \"Dependencies\",\n\t\t\t\tstatus: \"warn\",\n\t\t\t\tmessage: \"@screenbook/cli not found in dependencies\",\n\t\t\t\tsuggestion: \"Run 'pnpm add -D @screenbook/cli' to install\",\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tname: \"Dependencies\",\n\t\t\tstatus: \"pass\",\n\t\t\tmessage: `@screenbook/core@${coreVersion}, @screenbook/cli@${cliVersion}`,\n\t\t}\n\t} catch {\n\t\treturn {\n\t\t\tname: \"Dependencies\",\n\t\t\tstatus: \"fail\",\n\t\t\tmessage: \"Failed to read package.json\",\n\t\t\tsuggestion: \"Ensure package.json is valid JSON\",\n\t\t}\n\t}\n}\n\nexport async function checkMetaPattern(\n\tcwd: string,\n\tmetaPattern: string,\n\tignore: string[],\n): Promise<CheckResult> {\n\ttry {\n\t\tconst files = await glob(metaPattern, { cwd, ignore })\n\n\t\tif (files.length === 0) {\n\t\t\treturn {\n\t\t\t\tname: \"Screen meta files\",\n\t\t\t\tstatus: \"warn\",\n\t\t\t\tmessage: `No files matching: ${metaPattern}`,\n\t\t\t\tsuggestion: \"Run 'screenbook generate' to create screen.meta.ts files\",\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tname: \"Screen meta files\",\n\t\t\tstatus: \"pass\",\n\t\t\tmessage: `Found ${files.length} screen.meta.ts file${files.length > 1 ? \"s\" : \"\"}`,\n\t\t}\n\t} catch {\n\t\treturn {\n\t\t\tname: \"Screen meta files\",\n\t\t\tstatus: \"fail\",\n\t\t\tmessage: `Invalid pattern: ${metaPattern}`,\n\t\t\tsuggestion: \"Check metaPattern in your config file\",\n\t\t}\n\t}\n}\n\nexport async function checkRoutesPattern(\n\tcwd: string,\n\troutesPattern: string | undefined,\n\tignore: string[],\n): Promise<CheckResult> {\n\tif (!routesPattern) {\n\t\treturn {\n\t\t\tname: \"Routes pattern\",\n\t\t\tstatus: \"warn\",\n\t\t\tmessage: \"routesPattern not configured\",\n\t\t\tsuggestion:\n\t\t\t\t\"Set routesPattern in config to enable 'lint' and 'generate' commands\",\n\t\t}\n\t}\n\n\ttry {\n\t\tconst files = await glob(routesPattern, { cwd, ignore })\n\n\t\tif (files.length === 0) {\n\t\t\treturn {\n\t\t\t\tname: \"Routes pattern\",\n\t\t\t\tstatus: \"warn\",\n\t\t\t\tmessage: `No files matching: ${routesPattern}`,\n\t\t\t\tsuggestion: \"Check routesPattern in your config file\",\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tname: \"Routes pattern\",\n\t\t\tstatus: \"pass\",\n\t\t\tmessage: `Found ${files.length} route file${files.length > 1 ? \"s\" : \"\"}`,\n\t\t}\n\t} catch {\n\t\treturn {\n\t\t\tname: \"Routes pattern\",\n\t\t\tstatus: \"fail\",\n\t\t\tmessage: `Invalid pattern: ${routesPattern}`,\n\t\t\tsuggestion: \"Check routesPattern in your config file\",\n\t\t}\n\t}\n}\n\nexport async function checkBuildOutput(\n\tcwd: string,\n\toutDir: string,\n): Promise<CheckResult> {\n\tconst screensJsonPath = join(cwd, outDir, \"screens.json\")\n\n\tif (!existsSync(screensJsonPath)) {\n\t\treturn {\n\t\t\tname: \"Build output\",\n\t\t\tstatus: \"fail\",\n\t\t\tmessage: `screens.json not found in ${outDir}/`,\n\t\t\tsuggestion: \"Run 'screenbook build' to generate metadata\",\n\t\t}\n\t}\n\n\ttry {\n\t\tconst content = readFileSync(screensJsonPath, \"utf-8\")\n\t\tconst screens = JSON.parse(content) as unknown[]\n\n\t\treturn {\n\t\t\tname: \"Build output\",\n\t\t\tstatus: \"pass\",\n\t\t\tmessage: `screens.json contains ${screens.length} screen${screens.length > 1 ? \"s\" : \"\"}`,\n\t\t}\n\t} catch {\n\t\treturn {\n\t\t\tname: \"Build output\",\n\t\t\tstatus: \"fail\",\n\t\t\tmessage: \"screens.json is corrupted\",\n\t\t\tsuggestion: \"Run 'screenbook build' to regenerate\",\n\t\t}\n\t}\n}\n\nexport async function checkVersionCompatibility(\n\tcwd: string,\n): Promise<CheckResult> {\n\tconst packageJsonPath = join(cwd, \"package.json\")\n\n\tif (!existsSync(packageJsonPath)) {\n\t\treturn {\n\t\t\tname: \"Version compatibility\",\n\t\t\tstatus: \"warn\",\n\t\t\tmessage: \"Cannot check - package.json not found\",\n\t\t}\n\t}\n\n\ttry {\n\t\tconst content = readFileSync(packageJsonPath, \"utf-8\")\n\t\tconst pkg = JSON.parse(content) as PackageJson\n\n\t\tconst allDeps = { ...pkg.dependencies, ...pkg.devDependencies }\n\t\tconst unifiedVersion = allDeps.screenbook\n\t\tconst coreVersion = allDeps[\"@screenbook/core\"]\n\t\tconst cliVersion = allDeps[\"@screenbook/cli\"]\n\n\t\t// Unified package - no compatibility check needed\n\t\tif (unifiedVersion) {\n\t\t\treturn {\n\t\t\t\tname: \"Version compatibility\",\n\t\t\t\tstatus: \"pass\",\n\t\t\t\tmessage: \"Using unified screenbook package\",\n\t\t\t}\n\t\t}\n\n\t\tif (!coreVersion || !cliVersion) {\n\t\t\treturn {\n\t\t\t\tname: \"Version compatibility\",\n\t\t\t\tstatus: \"warn\",\n\t\t\t\tmessage: \"Cannot check - packages not installed\",\n\t\t\t}\n\t\t}\n\n\t\t// Extract major version (handle ^, ~, etc.)\n\t\tconst extractMajor = (version: string): string => {\n\t\t\tconst cleaned = version.replace(/^[\\^~>=<]+/, \"\")\n\t\t\treturn cleaned.split(\".\")[0] ?? \"0\"\n\t\t}\n\n\t\tconst coreMajor = extractMajor(coreVersion)\n\t\tconst cliMajor = extractMajor(cliVersion)\n\n\t\tif (coreMajor !== cliMajor) {\n\t\t\treturn {\n\t\t\t\tname: \"Version compatibility\",\n\t\t\t\tstatus: \"warn\",\n\t\t\t\tmessage: `Major version mismatch: core@${coreVersion} vs cli@${cliVersion}`,\n\t\t\t\tsuggestion: \"Update packages to matching major versions\",\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tname: \"Version compatibility\",\n\t\t\tstatus: \"pass\",\n\t\t\tmessage: \"Package versions are compatible\",\n\t\t}\n\t} catch {\n\t\treturn {\n\t\t\tname: \"Version compatibility\",\n\t\t\tstatus: \"fail\",\n\t\t\tmessage: \"Failed to read package.json\",\n\t\t}\n\t}\n}\n\nexport async function checkGitRepository(cwd: string): Promise<CheckResult> {\n\tconst gitDir = join(cwd, \".git\")\n\n\tif (!existsSync(gitDir)) {\n\t\treturn {\n\t\t\tname: \"Git repository\",\n\t\t\tstatus: \"warn\",\n\t\t\tmessage: \"Not a git repository\",\n\t\t\tsuggestion:\n\t\t\t\t\"Run 'git init' to enable 'pr-impact' command for PR analysis\",\n\t\t}\n\t}\n\n\treturn {\n\t\tname: \"Git repository\",\n\t\tstatus: \"pass\",\n\t\tmessage: \"Git repository detected\",\n\t}\n}\n\nfunction displayResults(results: CheckResult[], verbose: boolean): void {\n\tlet passCount = 0\n\tlet failCount = 0\n\tlet warnCount = 0\n\n\tfor (const result of results) {\n\t\tconst icon =\n\t\t\tresult.status === \"pass\"\n\t\t\t\t? logger.green(\"✓\")\n\t\t\t\t: result.status === \"fail\"\n\t\t\t\t\t? logger.red(\"✗\")\n\t\t\t\t\t: logger.yellow(\"⚠\")\n\n\t\tconst statusColor =\n\t\t\tresult.status === \"pass\"\n\t\t\t\t? logger.green\n\t\t\t\t: result.status === \"fail\"\n\t\t\t\t\t? logger.red\n\t\t\t\t\t: logger.yellow\n\n\t\tlogger.log(`${icon} ${statusColor(result.name)}: ${result.message}`)\n\n\t\tif (result.suggestion && (result.status !== \"pass\" || verbose)) {\n\t\t\tlogger.log(` ${logger.dim(\"→\")} ${result.suggestion}`)\n\t\t}\n\n\t\tif (result.status === \"pass\") passCount++\n\t\telse if (result.status === \"fail\") failCount++\n\t\telse warnCount++\n\t}\n\n\tlogger.log(\"\")\n\n\tconst summary: string[] = []\n\tif (passCount > 0) summary.push(logger.green(`${passCount} passed`))\n\tif (failCount > 0) summary.push(logger.red(`${failCount} failed`))\n\tif (warnCount > 0) summary.push(logger.yellow(`${warnCount} warnings`))\n\n\tlogger.log(`Summary: ${summary.join(\", \")}`)\n\n\tif (failCount > 0) {\n\t\tlogger.log(\"\")\n\t\tlogger.log(\n\t\t\tlogger.dim(\"Run the suggested commands above to fix the issues.\"),\n\t\t)\n\t}\n}\n","import { resolve } from \"node:path\"\n\n/**\n * Supported router types for auto-detection.\n * Detection order: TanStack Router -> Solid Router -> Angular Router -> React Router -> Vue Router.\n */\nexport type RouterType =\n\t| \"react-router\"\n\t| \"vue-router\"\n\t| \"tanstack-router\"\n\t| \"solid-router\"\n\t| \"angular-router\"\n\t| \"unknown\"\n\n/**\n * Parsed route from router config (Vue Router, React Router, etc.)\n */\nexport interface ParsedRoute {\n\tpath: string\n\tname?: string\n\tcomponent?: string\n\tchildren?: ParsedRoute[]\n\tredirect?: string\n}\n\n/**\n * Flattened route with computed properties\n */\nexport interface FlatRoute {\n\t/** Full path including parent paths */\n\tfullPath: string\n\t/** Route name if defined */\n\tname?: string\n\t/** Component file path if available */\n\tcomponentPath?: string\n\t/** Computed screen ID from path */\n\tscreenId: string\n\t/** Computed screen title from path */\n\tscreenTitle: string\n\t/** Nesting depth */\n\tdepth: number\n}\n\n/**\n * Result of parsing a router config file\n */\nexport interface ParseResult {\n\troutes: ParsedRoute[]\n\twarnings: string[]\n}\n\n/**\n * Resolve relative import path to absolute path\n */\nexport function resolveImportPath(importPath: string, baseDir: string): string {\n\tif (importPath.startsWith(\".\")) {\n\t\treturn resolve(baseDir, importPath)\n\t}\n\treturn importPath\n}\n\n/**\n * Flatten nested routes into a flat list with computed properties\n */\nexport function flattenRoutes(\n\troutes: ParsedRoute[],\n\tparentPath = \"\",\n\tdepth = 0,\n): FlatRoute[] {\n\tconst result: FlatRoute[] = []\n\n\tfor (const route of routes) {\n\t\t// Skip redirect-only routes\n\t\tif (route.redirect && !route.component) {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Compute full path\n\t\tlet fullPath: string\n\t\tif (route.path.startsWith(\"/\")) {\n\t\t\tfullPath = route.path\n\t\t} else if (parentPath === \"/\") {\n\t\t\tfullPath = `/${route.path}`\n\t\t} else {\n\t\t\tfullPath = parentPath ? `${parentPath}/${route.path}` : `/${route.path}`\n\t\t}\n\n\t\t// Normalize path\n\t\tfullPath = fullPath.replace(/\\/+/g, \"/\")\n\t\tif (fullPath !== \"/\" && fullPath.endsWith(\"/\")) {\n\t\t\tfullPath = fullPath.slice(0, -1)\n\t\t}\n\n\t\t// Handle empty path (index routes)\n\t\tif (fullPath === \"\") {\n\t\t\tfullPath = parentPath || \"/\"\n\t\t}\n\n\t\t// Only add routes with components (skip abstract parent routes)\n\t\tif (route.component || !route.children) {\n\t\t\tresult.push({\n\t\t\t\tfullPath,\n\t\t\t\tname: route.name,\n\t\t\t\tcomponentPath: route.component,\n\t\t\t\tscreenId: pathToScreenId(fullPath),\n\t\t\t\tscreenTitle: pathToScreenTitle(fullPath),\n\t\t\t\tdepth,\n\t\t\t})\n\t\t}\n\n\t\t// Process children\n\t\tif (route.children) {\n\t\t\tresult.push(...flattenRoutes(route.children, fullPath, depth + 1))\n\t\t}\n\t}\n\n\treturn result\n}\n\n/**\n * Convert route path to screen ID\n * /user/:id/profile -> user.id.profile\n */\nexport function pathToScreenId(path: string): string {\n\tif (path === \"/\" || path === \"\") {\n\t\treturn \"home\"\n\t}\n\n\treturn path\n\t\t.replace(/^\\//, \"\") // Remove leading slash\n\t\t.replace(/\\/$/, \"\") // Remove trailing slash\n\t\t.split(\"/\")\n\t\t.map((segment) => {\n\t\t\t// Convert :param to param\n\t\t\tif (segment.startsWith(\":\")) {\n\t\t\t\treturn segment.slice(1)\n\t\t\t}\n\t\t\t// Convert *catchall or ** to catchall\n\t\t\tif (segment.startsWith(\"*\")) {\n\t\t\t\t// Handle ** (Angular) as catchall\n\t\t\t\tif (segment === \"**\") {\n\t\t\t\t\treturn \"catchall\"\n\t\t\t\t}\n\t\t\t\treturn segment.slice(1) || \"catchall\"\n\t\t\t}\n\t\t\treturn segment\n\t\t})\n\t\t.join(\".\")\n}\n\n/**\n * Convert route path to screen title\n * /user/:id/profile -> Profile\n */\nexport function pathToScreenTitle(path: string): string {\n\tif (path === \"/\" || path === \"\") {\n\t\treturn \"Home\"\n\t}\n\n\tconst segments = path\n\t\t.replace(/^\\//, \"\")\n\t\t.replace(/\\/$/, \"\")\n\t\t.split(\"/\")\n\t\t.filter((s) => !s.startsWith(\":\") && !s.startsWith(\"*\"))\n\n\tconst lastSegment = segments[segments.length - 1] || \"Home\"\n\n\t// Convert kebab-case or snake_case to Title Case\n\treturn lastSegment\n\t\t.split(/[-_]/)\n\t\t.map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n\t\t.join(\" \")\n}\n","import { readFileSync } from \"node:fs\"\nimport { dirname, resolve } from \"node:path\"\nimport { parse } from \"@babel/parser\"\nimport {\n\ttype ParsedRoute,\n\ttype ParseResult,\n\tresolveImportPath,\n} from \"./routeParserUtils.js\"\n\n// Re-export shared types\nexport type { ParsedRoute, ParseResult }\n\n/**\n * Parse Angular Router configuration file and extract routes.\n * Supports both Standalone (Angular 14+) and NgModule patterns.\n *\n * Supported patterns:\n * - `export const routes: Routes = [...]`\n * - `const routes: Routes = [...]`\n * - `RouterModule.forRoot([...])`\n * - `RouterModule.forChild([...])`\n * - `export default [...]`\n * - `export default [...] satisfies Routes`\n *\n * @param filePath - Path to the router configuration file\n * @param preloadedContent - Optional pre-read file content. When provided, the file is not read from disk,\n * enabling testing with virtual content or avoiding duplicate file reads.\n * @returns ParseResult containing extracted routes and any warnings\n * @throws Error if the file cannot be read or contains syntax errors\n */\nexport function parseAngularRouterConfig(\n\tfilePath: string,\n\tpreloadedContent?: string,\n): ParseResult {\n\tconst absolutePath = resolve(filePath)\n\tconst routesFileDir = dirname(absolutePath)\n\tconst warnings: string[] = []\n\n\t// Read file with proper error handling (skip if content is preloaded)\n\tlet content: string\n\tif (preloadedContent !== undefined) {\n\t\tcontent = preloadedContent\n\t} else {\n\t\ttry {\n\t\t\tcontent = readFileSync(absolutePath, \"utf-8\")\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to read routes file \"${absolutePath}\": ${message}`,\n\t\t\t)\n\t\t}\n\t}\n\n\t// Parse with Babel - wrap for better error messages\n\tlet ast: ReturnType<typeof parse>\n\ttry {\n\t\tast = parse(content, {\n\t\t\tsourceType: \"module\",\n\t\t\tplugins: [\"typescript\", [\"decorators\", { decoratorsBeforeExport: true }]],\n\t\t})\n\t} catch (error) {\n\t\tif (error instanceof SyntaxError) {\n\t\t\tthrow new Error(\n\t\t\t\t`Syntax error in routes file \"${absolutePath}\": ${error.message}`,\n\t\t\t)\n\t\t}\n\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\tthrow new Error(`Failed to parse routes file \"${absolutePath}\": ${message}`)\n\t}\n\n\tconst routes: ParsedRoute[] = []\n\n\t// Find routes array in the AST\n\tfor (const node of ast.program.body) {\n\t\t// Handle: const routes: Routes = [...] or export const routes: Routes = [...]\n\t\tif (node.type === \"VariableDeclaration\") {\n\t\t\tfor (const decl of node.declarations) {\n\t\t\t\tif (\n\t\t\t\t\tdecl.id.type === \"Identifier\" &&\n\t\t\t\t\tdecl.init?.type === \"ArrayExpression\"\n\t\t\t\t) {\n\t\t\t\t\t// Check if it looks like a routes array (name contains \"route\" or has Routes type)\n\t\t\t\t\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\t\t\t\t\tconst typeAnnotation = (decl.id as any).typeAnnotation?.typeAnnotation\n\t\t\t\t\tconst isRoutesVariable =\n\t\t\t\t\t\tdecl.id.name.toLowerCase().includes(\"route\") ||\n\t\t\t\t\t\t(typeAnnotation?.type === \"TSTypeReference\" &&\n\t\t\t\t\t\t\ttypeAnnotation.typeName?.type === \"Identifier\" &&\n\t\t\t\t\t\t\ttypeAnnotation.typeName.name === \"Routes\")\n\t\t\t\t\tif (isRoutesVariable) {\n\t\t\t\t\t\tconst parsed = parseRoutesArray(decl.init, routesFileDir, warnings)\n\t\t\t\t\t\troutes.push(...parsed)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle: export const routes: Routes = [...]\n\t\tif (\n\t\t\tnode.type === \"ExportNamedDeclaration\" &&\n\t\t\tnode.declaration?.type === \"VariableDeclaration\"\n\t\t) {\n\t\t\tfor (const decl of node.declaration.declarations) {\n\t\t\t\tif (\n\t\t\t\t\tdecl.id.type === \"Identifier\" &&\n\t\t\t\t\tdecl.init?.type === \"ArrayExpression\"\n\t\t\t\t) {\n\t\t\t\t\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\t\t\t\t\tconst typeAnnotation = (decl.id as any).typeAnnotation?.typeAnnotation\n\t\t\t\t\tconst isRoutesVariable =\n\t\t\t\t\t\tdecl.id.name.toLowerCase().includes(\"route\") ||\n\t\t\t\t\t\t(typeAnnotation?.type === \"TSTypeReference\" &&\n\t\t\t\t\t\t\ttypeAnnotation.typeName?.type === \"Identifier\" &&\n\t\t\t\t\t\t\ttypeAnnotation.typeName.name === \"Routes\")\n\t\t\t\t\tif (isRoutesVariable) {\n\t\t\t\t\t\tconst parsed = parseRoutesArray(decl.init, routesFileDir, warnings)\n\t\t\t\t\t\troutes.push(...parsed)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle: export default [...] (less common but possible)\n\t\tif (\n\t\t\tnode.type === \"ExportDefaultDeclaration\" &&\n\t\t\tnode.declaration.type === \"ArrayExpression\"\n\t\t) {\n\t\t\tconst parsed = parseRoutesArray(node.declaration, routesFileDir, warnings)\n\t\t\troutes.push(...parsed)\n\t\t}\n\n\t\t// Handle: export default [...] satisfies Routes\n\t\tif (\n\t\t\tnode.type === \"ExportDefaultDeclaration\" &&\n\t\t\tnode.declaration.type === \"TSSatisfiesExpression\" &&\n\t\t\tnode.declaration.expression.type === \"ArrayExpression\"\n\t\t) {\n\t\t\tconst parsed = parseRoutesArray(\n\t\t\t\tnode.declaration.expression,\n\t\t\t\troutesFileDir,\n\t\t\t\twarnings,\n\t\t\t)\n\t\t\troutes.push(...parsed)\n\t\t}\n\n\t\t// Handle NgModule pattern: RouterModule.forRoot([...]) or RouterModule.forChild([...])\n\t\t// This can appear in @NgModule decorator or as a call expression\n\t\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\t\tlet classNode: any = null\n\t\tif (node.type === \"ClassDeclaration\") {\n\t\t\tclassNode = node\n\t\t} else if (\n\t\t\tnode.type === \"ExportNamedDeclaration\" &&\n\t\t\t// biome-ignore lint/suspicious/noExplicitAny: AST node types require type assertions\n\t\t\t(node as any).declaration?.type === \"ClassDeclaration\"\n\t\t) {\n\t\t\t// biome-ignore lint/suspicious/noExplicitAny: AST node types require type assertions\n\t\t\tclassNode = (node as any).declaration\n\t\t}\n\n\t\tif (classNode) {\n\t\t\tconst decorators = classNode.decorators || []\n\t\t\tfor (const decorator of decorators) {\n\t\t\t\tif (decorator.expression?.type === \"CallExpression\") {\n\t\t\t\t\tconst routesFromDecorator = extractRoutesFromNgModule(\n\t\t\t\t\t\tdecorator.expression,\n\t\t\t\t\t\troutesFileDir,\n\t\t\t\t\t\twarnings,\n\t\t\t\t\t)\n\t\t\t\t\troutes.push(...routesFromDecorator)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle standalone RouterModule.forRoot/forChild calls in exports\n\t\tif (node.type === \"ExpressionStatement\") {\n\t\t\tconst routesFromExpr = extractRoutesFromExpression(\n\t\t\t\tnode.expression,\n\t\t\t\troutesFileDir,\n\t\t\t\twarnings,\n\t\t\t)\n\t\t\troutes.push(...routesFromExpr)\n\t\t}\n\t}\n\n\t// Warn if no routes were found\n\tif (routes.length === 0) {\n\t\twarnings.push(\n\t\t\t\"No routes found. Supported patterns: 'export const routes: Routes = [...]', 'RouterModule.forRoot([...])', or 'RouterModule.forChild([...])'\",\n\t\t)\n\t}\n\n\treturn { routes, warnings }\n}\n\n/**\n * Extract routes from @NgModule decorator\n */\nfunction extractRoutesFromNgModule(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tcallExpr: any,\n\tbaseDir: string,\n\twarnings: string[],\n): ParsedRoute[] {\n\tconst routes: ParsedRoute[] = []\n\n\t// Check if it's @NgModule({...})\n\tif (\n\t\tcallExpr.callee?.type === \"Identifier\" &&\n\t\tcallExpr.callee.name === \"NgModule\"\n\t) {\n\t\tconst arg = callExpr.arguments[0]\n\t\tif (arg?.type === \"ObjectExpression\") {\n\t\t\tfor (const prop of arg.properties) {\n\t\t\t\tif (\n\t\t\t\t\tprop.type === \"ObjectProperty\" &&\n\t\t\t\t\tprop.key?.type === \"Identifier\" &&\n\t\t\t\t\tprop.key.name === \"imports\"\n\t\t\t\t) {\n\t\t\t\t\tif (prop.value?.type === \"ArrayExpression\") {\n\t\t\t\t\t\tfor (const element of prop.value.elements) {\n\t\t\t\t\t\t\tif (!element) continue\n\t\t\t\t\t\t\tconst extracted = extractRoutesFromExpression(\n\t\t\t\t\t\t\t\telement,\n\t\t\t\t\t\t\t\tbaseDir,\n\t\t\t\t\t\t\t\twarnings,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\troutes.push(...extracted)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn routes\n}\n\n/**\n * Extract routes from RouterModule.forRoot/forChild call expressions\n */\nfunction extractRoutesFromExpression(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): ParsedRoute[] {\n\tconst routes: ParsedRoute[] = []\n\n\tif (node?.type !== \"CallExpression\") return routes\n\n\tconst callee = node.callee\n\tif (\n\t\tcallee?.type === \"MemberExpression\" &&\n\t\tcallee.object?.type === \"Identifier\" &&\n\t\tcallee.object.name === \"RouterModule\" &&\n\t\tcallee.property?.type === \"Identifier\" &&\n\t\t(callee.property.name === \"forRoot\" || callee.property.name === \"forChild\")\n\t) {\n\t\tconst routesArg = node.arguments[0]\n\t\tif (routesArg?.type === \"ArrayExpression\") {\n\t\t\tconst parsed = parseRoutesArray(routesArg, baseDir, warnings)\n\t\t\troutes.push(...parsed)\n\t\t}\n\t}\n\n\treturn routes\n}\n\n/**\n * Parse an array expression containing route objects\n */\nfunction parseRoutesArray(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tarrayNode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): ParsedRoute[] {\n\tconst routes: ParsedRoute[] = []\n\n\tfor (const element of arrayNode.elements) {\n\t\tif (!element) continue\n\n\t\t// Handle spread elements\n\t\tif (element.type === \"SpreadElement\") {\n\t\t\tconst loc = element.loc ? ` at line ${element.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Spread operator detected${loc}. Routes from spread cannot be statically analyzed.`,\n\t\t\t)\n\t\t\tcontinue\n\t\t}\n\n\t\tif (element.type === \"ObjectExpression\") {\n\t\t\tconst parsedRoute = parseRouteObject(element, baseDir, warnings)\n\t\t\tif (parsedRoute) {\n\t\t\t\troutes.push(parsedRoute)\n\t\t\t}\n\t\t} else {\n\t\t\tconst loc = element.loc ? ` at line ${element.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Non-object route element (${element.type})${loc}. Only object literals can be statically analyzed.`,\n\t\t\t)\n\t\t}\n\t}\n\n\treturn routes\n}\n\n/**\n * Parse a single route object expression\n */\nfunction parseRouteObject(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tobjectNode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): ParsedRoute | null {\n\tlet path: string | undefined\n\tlet component: string | undefined\n\tlet children: ParsedRoute[] | undefined\n\tlet redirectTo: string | undefined\n\tlet hasPath = false\n\n\tfor (const prop of objectNode.properties) {\n\t\tif (prop.type !== \"ObjectProperty\") continue\n\t\tif (prop.key.type !== \"Identifier\") continue\n\n\t\tconst key = prop.key.name\n\n\t\tswitch (key) {\n\t\t\tcase \"path\":\n\t\t\t\tif (prop.value.type === \"StringLiteral\") {\n\t\t\t\t\tpath = prop.value.value\n\t\t\t\t\thasPath = true\n\t\t\t\t} else {\n\t\t\t\t\tconst loc = prop.loc ? ` at line ${prop.loc.start.line}` : \"\"\n\t\t\t\t\twarnings.push(\n\t\t\t\t\t\t`Dynamic path value (${prop.value.type})${loc}. Only string literal paths can be statically analyzed.`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\tcase \"component\":\n\t\t\t\t// Direct component reference: component: HomeComponent\n\t\t\t\tif (prop.value.type === \"Identifier\") {\n\t\t\t\t\tcomponent = prop.value.name\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\tcase \"loadComponent\":\n\t\t\t\t// Lazy component: loadComponent: () => import('./path').then(m => m.Component)\n\t\t\t\tcomponent = extractLazyComponent(prop.value, baseDir, warnings)\n\t\t\t\tbreak\n\n\t\t\tcase \"loadChildren\": {\n\t\t\t\t// Lazy children: loadChildren: () => import('./path').then(m => m.routes)\n\t\t\t\t// We just note this for now - the actual routes are in another file\n\t\t\t\tconst lazyPath = extractLazyPath(prop.value, baseDir, warnings)\n\t\t\t\tif (lazyPath) {\n\t\t\t\t\tcomponent = `[lazy: ${lazyPath}]`\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tcase \"children\":\n\t\t\t\tif (prop.value.type === \"ArrayExpression\") {\n\t\t\t\t\tchildren = parseRoutesArray(prop.value, baseDir, warnings)\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\tcase \"redirectTo\":\n\t\t\t\tif (prop.value.type === \"StringLiteral\") {\n\t\t\t\t\tredirectTo = prop.value.value\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\t// Skip these properties (not relevant for screen detection)\n\t\t\tcase \"pathMatch\":\n\t\t\tcase \"canActivate\":\n\t\t\tcase \"canDeactivate\":\n\t\t\tcase \"canMatch\":\n\t\t\tcase \"resolve\":\n\t\t\tcase \"data\":\n\t\t\tcase \"title\":\n\t\t\tcase \"providers\":\n\t\t\tcase \"runGuardsAndResolvers\":\n\t\t\tcase \"outlet\":\n\t\t\t\tbreak\n\t\t}\n\t}\n\n\t// Skip redirect-only routes (no component)\n\tif (redirectTo && !component && !children) {\n\t\treturn null\n\t}\n\n\t// Skip routes without path (unless they have children for layout purposes)\n\tif (!hasPath) {\n\t\tif (children && children.length > 0) {\n\t\t\treturn { path: \"\", component, children }\n\t\t}\n\t\treturn null\n\t}\n\n\treturn {\n\t\tpath: path || \"\",\n\t\tcomponent,\n\t\tchildren,\n\t}\n}\n\n/**\n * Extract component from lazy loadComponent pattern\n * loadComponent: () => import('./path').then(m => m.Component)\n */\nfunction extractLazyComponent(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): string | undefined {\n\t// Arrow function: () => import('./path').then(m => m.Component)\n\tif (node.type === \"ArrowFunctionExpression\") {\n\t\tconst body = node.body\n\n\t\t// Handle .then(m => m.Component) chain\n\t\tif (\n\t\t\tbody.type === \"CallExpression\" &&\n\t\t\tbody.callee?.type === \"MemberExpression\" &&\n\t\t\tbody.callee.property?.type === \"Identifier\" &&\n\t\t\tbody.callee.property.name === \"then\"\n\t\t) {\n\t\t\tconst importCall = body.callee.object\n\t\t\tconst thenArg = body.arguments[0]\n\n\t\t\t// Check if it's an import() call\n\t\t\tif (\n\t\t\t\timportCall?.type === \"CallExpression\" &&\n\t\t\t\timportCall.callee?.type === \"Import\"\n\t\t\t) {\n\t\t\t\t// Check if the argument is a string literal\n\t\t\t\tif (importCall.arguments[0]?.type === \"StringLiteral\") {\n\t\t\t\t\tconst importPath = resolveImportPath(\n\t\t\t\t\t\timportCall.arguments[0].value,\n\t\t\t\t\t\tbaseDir,\n\t\t\t\t\t)\n\n\t\t\t\t\t// Extract component name from .then(m => m.Component)\n\t\t\t\t\tif (\n\t\t\t\t\t\tthenArg?.type === \"ArrowFunctionExpression\" &&\n\t\t\t\t\t\tthenArg.body?.type === \"MemberExpression\" &&\n\t\t\t\t\t\tthenArg.body.property?.type === \"Identifier\"\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn `${importPath}#${thenArg.body.property.name}`\n\t\t\t\t\t}\n\n\t\t\t\t\treturn importPath\n\t\t\t\t}\n\t\t\t\t// Dynamic import path - warn the user\n\t\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Lazy loadComponent with dynamic path${loc}. Only string literal imports can be analyzed.`,\n\t\t\t\t)\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t}\n\n\t\t// Direct import without .then(): () => import('./path')\n\t\tif (body.type === \"CallExpression\" && body.callee?.type === \"Import\") {\n\t\t\tif (body.arguments[0]?.type === \"StringLiteral\") {\n\t\t\t\treturn resolveImportPath(body.arguments[0].value, baseDir)\n\t\t\t}\n\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Lazy loadComponent with dynamic path${loc}. Only string literal imports can be analyzed.`,\n\t\t\t)\n\t\t\treturn undefined\n\t\t}\n\t}\n\n\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\twarnings.push(\n\t\t`Unrecognized loadComponent pattern (${node.type})${loc}. Expected arrow function with import().then().`,\n\t)\n\treturn undefined\n}\n\n/**\n * Extract path from lazy loadChildren pattern\n * loadChildren: () => import('./path').then(m => m.routes)\n */\nfunction extractLazyPath(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): string | undefined {\n\tif (node.type === \"ArrowFunctionExpression\") {\n\t\tconst body = node.body\n\n\t\t// Handle .then() chain\n\t\tif (\n\t\t\tbody.type === \"CallExpression\" &&\n\t\t\tbody.callee?.type === \"MemberExpression\" &&\n\t\t\tbody.callee.property?.type === \"Identifier\" &&\n\t\t\tbody.callee.property.name === \"then\"\n\t\t) {\n\t\t\tconst importCall = body.callee.object\n\t\t\tif (\n\t\t\t\timportCall?.type === \"CallExpression\" &&\n\t\t\t\timportCall.callee?.type === \"Import\" &&\n\t\t\t\timportCall.arguments[0]?.type === \"StringLiteral\"\n\t\t\t) {\n\t\t\t\treturn resolveImportPath(importCall.arguments[0].value, baseDir)\n\t\t\t}\n\t\t}\n\n\t\t// Direct import without .then()\n\t\tif (body.type === \"CallExpression\" && body.callee?.type === \"Import\") {\n\t\t\tif (body.arguments[0]?.type === \"StringLiteral\") {\n\t\t\t\treturn resolveImportPath(body.arguments[0].value, baseDir)\n\t\t\t}\n\t\t}\n\t}\n\n\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\twarnings.push(\n\t\t`Unrecognized loadChildren pattern (${node.type})${loc}. Expected arrow function with import().`,\n\t)\n\treturn undefined\n}\n\n/**\n * Detect if content is Angular Router based on patterns.\n * Checks for @angular/router import, RouterModule patterns, or Routes type annotation.\n */\nexport function isAngularRouterContent(content: string): boolean {\n\t// Check for Angular Router import\n\tif (content.includes(\"@angular/router\")) {\n\t\treturn true\n\t}\n\n\t// Check for RouterModule patterns\n\tif (\n\t\tcontent.includes(\"RouterModule.forRoot\") ||\n\t\tcontent.includes(\"RouterModule.forChild\")\n\t) {\n\t\treturn true\n\t}\n\n\t// Check for Routes type annotation: : Routes = or : Routes[\n\tif (/:\\s*Routes\\s*[=[]/.test(content)) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n","import { readFileSync } from \"node:fs\"\nimport { dirname, resolve } from \"node:path\"\nimport { parse } from \"@babel/parser\"\nimport {\n\ttype ParsedRoute,\n\ttype ParseResult,\n\tresolveImportPath,\n} from \"./routeParserUtils.js\"\n\n// Re-export shared types\nexport type { ParsedRoute, ParseResult }\n\n/**\n * Parse Solid Router configuration file and extract routes.\n * Supports various export patterns including `export const routes`, `export default`,\n * and TypeScript's `satisfies` operator.\n *\n * @param filePath - Path to the router configuration file\n * @param preloadedContent - Optional pre-read file content to avoid duplicate file reads\n * @returns ParseResult containing extracted routes and any warnings\n * @throws Error if the file cannot be read or contains syntax errors\n */\nexport function parseSolidRouterConfig(\n\tfilePath: string,\n\tpreloadedContent?: string,\n): ParseResult {\n\tconst absolutePath = resolve(filePath)\n\tconst routesFileDir = dirname(absolutePath)\n\tconst warnings: string[] = []\n\n\t// Read file with proper error handling (skip if content is preloaded)\n\tlet content: string\n\tif (preloadedContent !== undefined) {\n\t\tcontent = preloadedContent\n\t} else {\n\t\ttry {\n\t\t\tcontent = readFileSync(absolutePath, \"utf-8\")\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to read routes file \"${absolutePath}\": ${message}`,\n\t\t\t)\n\t\t}\n\t}\n\n\t// Parse with Babel - wrap for better error messages\n\tlet ast: ReturnType<typeof parse>\n\ttry {\n\t\tast = parse(content, {\n\t\t\tsourceType: \"module\",\n\t\t\tplugins: [\"typescript\", \"jsx\"],\n\t\t})\n\t} catch (error) {\n\t\tif (error instanceof SyntaxError) {\n\t\t\tthrow new Error(\n\t\t\t\t`Syntax error in routes file \"${absolutePath}\": ${error.message}`,\n\t\t\t)\n\t\t}\n\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\tthrow new Error(`Failed to parse routes file \"${absolutePath}\": ${message}`)\n\t}\n\n\tconst routes: ParsedRoute[] = []\n\n\t// Find routes array in the AST\n\tfor (const node of ast.program.body) {\n\t\t// Handle: const routes = [...]\n\t\tif (node.type === \"VariableDeclaration\") {\n\t\t\tfor (const decl of node.declarations) {\n\t\t\t\tif (\n\t\t\t\t\tdecl.id.type === \"Identifier\" &&\n\t\t\t\t\tdecl.id.name === \"routes\" &&\n\t\t\t\t\tdecl.init?.type === \"ArrayExpression\"\n\t\t\t\t) {\n\t\t\t\t\tconst parsed = parseRoutesArray(decl.init, routesFileDir, warnings)\n\t\t\t\t\troutes.push(...parsed)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle: export const routes = [...]\n\t\tif (\n\t\t\tnode.type === \"ExportNamedDeclaration\" &&\n\t\t\tnode.declaration?.type === \"VariableDeclaration\"\n\t\t) {\n\t\t\tfor (const decl of node.declaration.declarations) {\n\t\t\t\tif (\n\t\t\t\t\tdecl.id.type === \"Identifier\" &&\n\t\t\t\t\tdecl.id.name === \"routes\" &&\n\t\t\t\t\tdecl.init?.type === \"ArrayExpression\"\n\t\t\t\t) {\n\t\t\t\t\tconst parsed = parseRoutesArray(decl.init, routesFileDir, warnings)\n\t\t\t\t\troutes.push(...parsed)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle: export default [...]\n\t\tif (\n\t\t\tnode.type === \"ExportDefaultDeclaration\" &&\n\t\t\tnode.declaration.type === \"ArrayExpression\"\n\t\t) {\n\t\t\tconst parsed = parseRoutesArray(node.declaration, routesFileDir, warnings)\n\t\t\troutes.push(...parsed)\n\t\t}\n\n\t\t// Handle: export default [...] satisfies RouteDefinition[]\n\t\tif (\n\t\t\tnode.type === \"ExportDefaultDeclaration\" &&\n\t\t\tnode.declaration.type === \"TSSatisfiesExpression\" &&\n\t\t\tnode.declaration.expression.type === \"ArrayExpression\"\n\t\t) {\n\t\t\tconst parsed = parseRoutesArray(\n\t\t\t\tnode.declaration.expression,\n\t\t\t\troutesFileDir,\n\t\t\t\twarnings,\n\t\t\t)\n\t\t\troutes.push(...parsed)\n\t\t}\n\t}\n\n\t// Warn if no routes were found\n\tif (routes.length === 0) {\n\t\twarnings.push(\n\t\t\t\"No routes found. Supported patterns: 'export const routes = [...]' or 'export default [...]'\",\n\t\t)\n\t}\n\n\treturn { routes, warnings }\n}\n\n/**\n * Parse an array expression containing route objects\n */\nfunction parseRoutesArray(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tarrayNode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): ParsedRoute[] {\n\tconst routes: ParsedRoute[] = []\n\n\tfor (const element of arrayNode.elements) {\n\t\tif (!element) continue\n\n\t\t// Handle spread elements\n\t\tif (element.type === \"SpreadElement\") {\n\t\t\tconst loc = element.loc ? ` at line ${element.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Spread operator detected${loc}. Routes from spread cannot be statically analyzed.`,\n\t\t\t)\n\t\t\tcontinue\n\t\t}\n\n\t\tif (element.type === \"ObjectExpression\") {\n\t\t\tconst parsedRoutes = parseRouteObject(element, baseDir, warnings)\n\t\t\troutes.push(...parsedRoutes)\n\t\t} else {\n\t\t\tconst loc = element.loc ? ` at line ${element.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Non-object route element (${element.type})${loc}. Only object literals can be statically analyzed.`,\n\t\t\t)\n\t\t}\n\t}\n\n\treturn routes\n}\n\n/**\n * Parse a single route object expression\n * Returns array to handle multiple paths case: path: [\"/a\", \"/b\"]\n */\nfunction parseRouteObject(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tobjectNode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): ParsedRoute[] {\n\tlet paths: string[] = []\n\tlet component: string | undefined\n\tlet children: ParsedRoute[] | undefined\n\tlet hasPath = false\n\n\tfor (const prop of objectNode.properties) {\n\t\tif (prop.type !== \"ObjectProperty\") continue\n\t\tif (prop.key.type !== \"Identifier\") continue\n\n\t\tconst key = prop.key.name\n\n\t\tswitch (key) {\n\t\t\tcase \"path\":\n\t\t\t\tif (prop.value.type === \"StringLiteral\") {\n\t\t\t\t\tpaths = [prop.value.value]\n\t\t\t\t\thasPath = true\n\t\t\t\t} else if (prop.value.type === \"ArrayExpression\") {\n\t\t\t\t\t// Solid Router supports path arrays: path: [\"/login\", \"/register\"]\n\t\t\t\t\tconst arrayElementCount = prop.value.elements.filter(Boolean).length\n\t\t\t\t\tpaths = extractPathArray(prop.value, warnings)\n\t\t\t\t\thasPath = paths.length > 0\n\t\t\t\t\t// Warn if path array had elements but none were extractable\n\t\t\t\t\tif (arrayElementCount > 0 && paths.length === 0) {\n\t\t\t\t\t\tconst loc = prop.loc ? ` at line ${prop.loc.start.line}` : \"\"\n\t\t\t\t\t\twarnings.push(\n\t\t\t\t\t\t\t`Path array contains only dynamic values${loc}. No static paths could be extracted.`,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tconst loc = prop.loc ? ` at line ${prop.loc.start.line}` : \"\"\n\t\t\t\t\twarnings.push(\n\t\t\t\t\t\t`Dynamic path value (${prop.value.type})${loc}. Only string literal paths can be statically analyzed.`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\tcase \"component\":\n\t\t\t\tcomponent = extractComponent(prop.value, baseDir, warnings)\n\t\t\t\tbreak\n\n\t\t\tcase \"children\":\n\t\t\t\tif (prop.value.type === \"ArrayExpression\") {\n\t\t\t\t\tchildren = parseRoutesArray(prop.value, baseDir, warnings)\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\t// preload and matchFilters are ignored (not relevant for screen detection)\n\t\t}\n\t}\n\n\t// Skip routes without path (abstract layout wrappers)\n\tif (!hasPath) {\n\t\t// If it has children, process them with empty parent path contribution\n\t\tif (children && children.length > 0) {\n\t\t\treturn [{ path: \"\", component, children }]\n\t\t}\n\t\treturn []\n\t}\n\n\t// Create routes for each path (handles path arrays)\n\treturn paths.map((path) => ({\n\t\tpath,\n\t\tcomponent,\n\t\tchildren,\n\t}))\n}\n\n/**\n * Extract paths from array expression\n * path: [\"/login\", \"/register\"] -> [\"/login\", \"/register\"]\n */\nfunction extractPathArray(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tarrayNode: any,\n\twarnings: string[],\n): string[] {\n\tconst paths: string[] = []\n\n\tfor (const element of arrayNode.elements) {\n\t\tif (!element) continue\n\n\t\tif (element.type === \"StringLiteral\") {\n\t\t\tpaths.push(element.value)\n\t\t} else {\n\t\t\tconst loc = element.loc ? ` at line ${element.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Non-string path in array (${element.type})${loc}. Only string literal paths can be analyzed.`,\n\t\t\t)\n\t\t}\n\t}\n\n\treturn paths\n}\n\n/**\n * Extract component from various patterns\n * - Direct identifier: component: Home\n * - Lazy component: component: lazy(() => import(\"./Home\"))\n */\nfunction extractComponent(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): string | undefined {\n\t// Direct component reference: component: Home\n\tif (node.type === \"Identifier\") {\n\t\treturn node.name\n\t}\n\n\t// Lazy component: component: lazy(() => import(\"./path\"))\n\tif (node.type === \"CallExpression\") {\n\t\tconst callee = node.callee\n\t\tif (callee.type === \"Identifier\" && callee.name === \"lazy\") {\n\t\t\tconst lazyArg = node.arguments[0]\n\t\t\tif (lazyArg) {\n\t\t\t\treturn extractLazyImportPath(lazyArg, baseDir, warnings)\n\t\t\t}\n\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`lazy() called without arguments${loc}. Expected arrow function with import().`,\n\t\t\t)\n\t\t\treturn undefined\n\t\t}\n\t\t// Other call expressions not supported - add warning\n\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\tconst calleeName = callee.type === \"Identifier\" ? callee.name : \"unknown\"\n\t\twarnings.push(\n\t\t\t`Unrecognized component pattern: ${calleeName}(...)${loc}. Only 'lazy(() => import(...))' is supported.`,\n\t\t)\n\t\treturn undefined\n\t}\n\n\t// Arrow function component: component: () => <Home />\n\tif (node.type === \"ArrowFunctionExpression\") {\n\t\tif (node.body.type === \"JSXElement\") {\n\t\t\tconst openingElement = node.body.openingElement\n\t\t\tif (openingElement?.name?.type === \"JSXIdentifier\") {\n\t\t\t\treturn openingElement.name.name\n\t\t\t}\n\t\t\t// JSXMemberExpression (namespaced components like <UI.Button />)\n\t\t\tif (openingElement?.name?.type === \"JSXMemberExpression\") {\n\t\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Namespaced JSX component (e.g., <UI.Button />)${loc}. Component extraction not supported for member expressions. Consider using a direct component reference or create a wrapper component.`,\n\t\t\t\t)\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t}\n\t\t// Block body arrow functions\n\t\tif (node.body.type === \"BlockStatement\") {\n\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Arrow function with block body${loc}. Only concise arrow functions returning JSX directly can be analyzed.`,\n\t\t\t)\n\t\t\treturn undefined\n\t\t}\n\t\t// JSX Fragments\n\t\tif (node.body.type === \"JSXFragment\") {\n\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`JSX Fragment detected${loc}. Cannot extract component name from fragments.`,\n\t\t\t)\n\t\t\treturn undefined\n\t\t}\n\t\t// Conditional expressions\n\t\tif (node.body.type === \"ConditionalExpression\") {\n\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\t// Try to extract component names from both branches for context\n\t\t\tlet componentInfo = \"\"\n\t\t\tconst consequent = node.body.consequent\n\t\t\tconst alternate = node.body.alternate\n\t\t\tif (\n\t\t\t\tconsequent?.type === \"JSXElement\" &&\n\t\t\t\talternate?.type === \"JSXElement\"\n\t\t\t) {\n\t\t\t\tconst consName = consequent.openingElement?.name?.name || \"unknown\"\n\t\t\t\tconst altName = alternate.openingElement?.name?.name || \"unknown\"\n\t\t\t\tcomponentInfo = ` (${consName} or ${altName})`\n\t\t\t}\n\t\t\twarnings.push(\n\t\t\t\t`Conditional component${componentInfo}${loc}. Only static JSX elements can be analyzed. Consider extracting to a separate component.`,\n\t\t\t)\n\t\t\treturn undefined\n\t\t}\n\t\t// Unrecognized arrow function body\n\t\tconst arrowLoc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\twarnings.push(\n\t\t\t`Unrecognized arrow function body (${node.body.type})${arrowLoc}. Component will not be extracted.`,\n\t\t)\n\t\treturn undefined\n\t}\n\n\t// Catch-all for unrecognized component patterns\n\tif (node) {\n\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\twarnings.push(\n\t\t\t`Unrecognized component pattern (${node.type})${loc}. Component will not be extracted.`,\n\t\t)\n\t}\n\treturn undefined\n}\n\n/**\n * Extract import path from lazy function argument\n * () => import(\"./pages/Dashboard\") -> resolved path\n */\nfunction extractLazyImportPath(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): string | undefined {\n\t// Arrow function: () => import('./path')\n\tif (node.type === \"ArrowFunctionExpression\") {\n\t\tconst body = node.body\n\n\t\tif (body.type === \"CallExpression\" && body.callee.type === \"Import\") {\n\t\t\tif (body.arguments[0]?.type === \"StringLiteral\") {\n\t\t\t\treturn resolveImportPath(body.arguments[0].value, baseDir)\n\t\t\t}\n\t\t\t// Dynamic import argument\n\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Lazy import with dynamic path${loc}. Only string literal imports can be analyzed.`,\n\t\t\t)\n\t\t\treturn undefined\n\t\t}\n\t}\n\n\t// Unrecognized lazy pattern\n\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\twarnings.push(\n\t\t`Unrecognized lazy pattern (${node.type})${loc}. Expected arrow function with import().`,\n\t)\n\treturn undefined\n}\n\n/**\n * Detect if content is Solid Router based on patterns.\n * Note: Called by detectRouterType() in reactRouterParser.ts before React Router detection\n * because Solid Router and React Router share similar syntax patterns (both use `path` and `component`).\n * The detection order matters: TanStack Router -> Solid Router -> Angular Router -> React Router -> Vue Router.\n */\nexport function isSolidRouterContent(content: string): boolean {\n\t// Check for Solid Router specific import\n\tif (content.includes(\"@solidjs/router\")) {\n\t\treturn true\n\t}\n\n\t// Check for old package name\n\tif (content.includes(\"solid-app-router\")) {\n\t\treturn true\n\t}\n\n\t// Check for Solid.js lazy with route pattern\n\t// This is a weaker check, so we need to be more specific\n\tif (\n\t\tcontent.includes(\"solid-js\") &&\n\t\t/\\blazy\\s*\\(/.test(content) &&\n\t\t/\\bcomponent\\s*:/.test(content) &&\n\t\t/\\bpath\\s*:/.test(content)\n\t) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n","import { readFileSync } from \"node:fs\"\nimport { dirname, resolve } from \"node:path\"\nimport { parse } from \"@babel/parser\"\nimport {\n\ttype ParsedRoute,\n\ttype ParseResult,\n\tresolveImportPath,\n} from \"./routeParserUtils.js\"\n\n// Re-export shared types\nexport type { ParsedRoute, ParseResult }\n\n/**\n * Internal route definition collected from createRoute/createRootRoute calls\n */\ninterface RouteDefinition {\n\tvariableName: string\n\tpath?: string\n\tcomponent?: string\n\tparentVariableName?: string\n\tisRoot: boolean\n\tchildren?: string[] // Variable names of child routes from addChildren\n}\n\n/**\n * Parse TanStack Router configuration file and extract routes\n */\nexport function parseTanStackRouterConfig(\n\tfilePath: string,\n\tpreloadedContent?: string,\n): ParseResult {\n\tconst absolutePath = resolve(filePath)\n\tconst routesFileDir = dirname(absolutePath)\n\tconst warnings: string[] = []\n\n\t// Read file with proper error handling (skip if content is preloaded)\n\tlet content: string\n\tif (preloadedContent !== undefined) {\n\t\tcontent = preloadedContent\n\t} else {\n\t\ttry {\n\t\t\tcontent = readFileSync(absolutePath, \"utf-8\")\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to read routes file \"${absolutePath}\": ${message}`,\n\t\t\t)\n\t\t}\n\t}\n\n\t// Parse with Babel - wrap for better error messages\n\tlet ast: ReturnType<typeof parse>\n\ttry {\n\t\tast = parse(content, {\n\t\t\tsourceType: \"module\",\n\t\t\tplugins: [\"typescript\", \"jsx\"],\n\t\t})\n\t} catch (error) {\n\t\tif (error instanceof SyntaxError) {\n\t\t\tthrow new Error(\n\t\t\t\t`Syntax error in routes file \"${absolutePath}\": ${error.message}`,\n\t\t\t)\n\t\t}\n\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\tthrow new Error(`Failed to parse routes file \"${absolutePath}\": ${message}`)\n\t}\n\n\t// Collect all route definitions from the AST\n\tconst routeMap = new Map<string, RouteDefinition>()\n\n\t// Two-pass AST processing is required because TanStack Router uses a function-based API\n\t// where routes are defined as variables and then composed using .addChildren().\n\t// Pass 1 must collect all route definitions first, so Pass 2 can resolve variable references.\n\n\t// First pass: collect all createRoute/createRootRoute calls\n\tfor (const node of ast.program.body) {\n\t\tcollectRouteDefinitions(node, routeMap, routesFileDir, warnings)\n\t}\n\n\t// Second pass: process addChildren calls to build parent-child relationships\n\tfor (const node of ast.program.body) {\n\t\tprocessAddChildrenCalls(node, routeMap, warnings)\n\t}\n\n\t// Build the route tree from collected definitions\n\tconst routes = buildRouteTree(routeMap, warnings)\n\n\t// Warn if no routes were found\n\tif (routes.length === 0) {\n\t\twarnings.push(\n\t\t\t\"No routes found. Supported patterns: 'createRootRoute()', 'createRoute()', and '.addChildren([...])'\",\n\t\t)\n\t}\n\n\treturn { routes, warnings }\n}\n\n/**\n * Collect route definitions from AST nodes\n */\nfunction collectRouteDefinitions(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\trouteMap: Map<string, RouteDefinition>,\n\tbaseDir: string,\n\twarnings: string[],\n): void {\n\t// Handle: const xxxRoute = createRoute({ ... }) or createRootRoute({ ... })\n\tif (node.type === \"VariableDeclaration\") {\n\t\tfor (const decl of node.declarations) {\n\t\t\tif (decl.id.type !== \"Identifier\") continue\n\n\t\t\tconst variableName = decl.id.name\n\n\t\t\t// Handle direct createRoute/createRootRoute call\n\t\t\tif (decl.init?.type === \"CallExpression\") {\n\t\t\t\tconst routeDef = extractRouteFromCallExpression(\n\t\t\t\t\tdecl.init,\n\t\t\t\t\tvariableName,\n\t\t\t\t\tbaseDir,\n\t\t\t\t\twarnings,\n\t\t\t\t)\n\t\t\t\tif (routeDef) {\n\t\t\t\t\trouteMap.set(variableName, routeDef)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Handle createRoute(...).lazy(...) pattern\n\t\t\tif (\n\t\t\t\tdecl.init?.type === \"CallExpression\" &&\n\t\t\t\tdecl.init.callee?.type === \"MemberExpression\" &&\n\t\t\t\tdecl.init.callee.property?.type === \"Identifier\" &&\n\t\t\t\tdecl.init.callee.property.name === \"lazy\"\n\t\t\t) {\n\t\t\t\t// The createRoute call is in callee.object\n\t\t\t\tconst createRouteCall = decl.init.callee.object\n\t\t\t\tif (createRouteCall?.type === \"CallExpression\") {\n\t\t\t\t\tconst routeDef = extractRouteFromCallExpression(\n\t\t\t\t\t\tcreateRouteCall,\n\t\t\t\t\t\tvariableName,\n\t\t\t\t\t\tbaseDir,\n\t\t\t\t\t\twarnings,\n\t\t\t\t\t)\n\t\t\t\t\tif (routeDef) {\n\t\t\t\t\t\t// Extract lazy import path\n\t\t\t\t\t\tconst lazyArg = decl.init.arguments[0]\n\t\t\t\t\t\tif (lazyArg) {\n\t\t\t\t\t\t\tconst lazyPath = extractLazyImportPath(lazyArg, baseDir, warnings)\n\t\t\t\t\t\t\tif (lazyPath) {\n\t\t\t\t\t\t\t\trouteDef.component = lazyPath\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\trouteMap.set(variableName, routeDef)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Handle: export const xxxRoute = createRoute({ ... })\n\tif (\n\t\tnode.type === \"ExportNamedDeclaration\" &&\n\t\tnode.declaration?.type === \"VariableDeclaration\"\n\t) {\n\t\tfor (const decl of node.declaration.declarations) {\n\t\t\tif (decl.id.type !== \"Identifier\") continue\n\n\t\t\tconst variableName = decl.id.name\n\n\t\t\tif (decl.init?.type === \"CallExpression\") {\n\t\t\t\tconst routeDef = extractRouteFromCallExpression(\n\t\t\t\t\tdecl.init,\n\t\t\t\t\tvariableName,\n\t\t\t\t\tbaseDir,\n\t\t\t\t\twarnings,\n\t\t\t\t)\n\t\t\t\tif (routeDef) {\n\t\t\t\t\trouteMap.set(variableName, routeDef)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Extract route definition from a CallExpression (createRoute or createRootRoute)\n */\nfunction extractRouteFromCallExpression(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tcallNode: any,\n\tvariableName: string,\n\tbaseDir: string,\n\twarnings: string[],\n): RouteDefinition | null {\n\tconst callee = callNode.callee\n\n\t// Check if it's createRoute or createRootRoute\n\tlet isRoot = false\n\tlet optionsArg = callNode.arguments[0]\n\n\tif (callee.type === \"Identifier\") {\n\t\tif (callee.name === \"createRootRoute\") {\n\t\t\tisRoot = true\n\t\t} else if (callee.name === \"createRootRouteWithContext\") {\n\t\t\tisRoot = true\n\t\t} else if (callee.name !== \"createRoute\") {\n\t\t\treturn null\n\t\t}\n\t} else if (callee.type === \"CallExpression\") {\n\t\t// Handle curried form: createRootRouteWithContext<T>()({...})\n\t\tconst innerCallee = callee.callee\n\t\tif (\n\t\t\tinnerCallee?.type === \"Identifier\" &&\n\t\t\tinnerCallee.name === \"createRootRouteWithContext\"\n\t\t) {\n\t\t\tisRoot = true\n\t\t\t// Options are in the outer call's arguments\n\t\t\toptionsArg = callNode.arguments[0]\n\t\t} else {\n\t\t\treturn null\n\t\t}\n\t} else {\n\t\treturn null\n\t}\n\n\tconst routeDef: RouteDefinition = {\n\t\tvariableName,\n\t\tisRoot,\n\t}\n\tif (optionsArg?.type === \"ObjectExpression\") {\n\t\tfor (const prop of optionsArg.properties) {\n\t\t\tif (prop.type !== \"ObjectProperty\") continue\n\t\t\tif (prop.key.type !== \"Identifier\") continue\n\n\t\t\tconst key = prop.key.name\n\n\t\t\tswitch (key) {\n\t\t\t\tcase \"path\":\n\t\t\t\t\tif (prop.value.type === \"StringLiteral\") {\n\t\t\t\t\t\t// Normalize TanStack Router path: $param -> :param\n\t\t\t\t\t\trouteDef.path = normalizeTanStackPath(prop.value.value)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconst loc = prop.loc ? ` at line ${prop.loc.start.line}` : \"\"\n\t\t\t\t\t\twarnings.push(\n\t\t\t\t\t\t\t`Dynamic path value (${prop.value.type})${loc}. Only string literal paths can be statically analyzed.`,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\n\t\t\t\tcase \"component\":\n\t\t\t\t\trouteDef.component = extractComponentValue(\n\t\t\t\t\t\tprop.value,\n\t\t\t\t\t\tbaseDir,\n\t\t\t\t\t\twarnings,\n\t\t\t\t\t)\n\t\t\t\t\tbreak\n\n\t\t\t\tcase \"getParentRoute\":\n\t\t\t\t\t// Extract parent route variable name from arrow function\n\t\t\t\t\tif (prop.value.type === \"ArrowFunctionExpression\") {\n\t\t\t\t\t\tconst body = prop.value.body\n\t\t\t\t\t\tif (body.type === \"Identifier\") {\n\t\t\t\t\t\t\trouteDef.parentVariableName = body.name\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tconst loc = prop.loc ? ` at line ${prop.loc.start.line}` : \"\"\n\t\t\t\t\t\t\twarnings.push(\n\t\t\t\t\t\t\t\t`Dynamic getParentRoute${loc}. Only static route references can be analyzed.`,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\treturn routeDef\n}\n\n/**\n * Extract component value from different patterns\n * Returns undefined with a warning for unrecognized patterns\n */\nfunction extractComponentValue(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): string | undefined {\n\t// Direct component reference: component: Home\n\tif (node.type === \"Identifier\") {\n\t\treturn node.name\n\t}\n\n\t// lazyRouteComponent(() => import('./path'))\n\tif (node.type === \"CallExpression\") {\n\t\tconst callee = node.callee\n\t\tif (callee.type === \"Identifier\" && callee.name === \"lazyRouteComponent\") {\n\t\t\tconst importArg = node.arguments[0]\n\t\t\tif (importArg) {\n\t\t\t\treturn extractLazyImportPath(importArg, baseDir, warnings)\n\t\t\t}\n\t\t\t// lazyRouteComponent called without arguments\n\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`lazyRouteComponent called without arguments${loc}. Expected arrow function with import().`,\n\t\t\t)\n\t\t\treturn undefined\n\t\t}\n\t\t// Other call expressions are not supported\n\t\treturn undefined\n\t}\n\n\t// Arrow function component: component: () => <Home />\n\tif (node.type === \"ArrowFunctionExpression\") {\n\t\t// Check if body is JSX\n\t\tif (node.body.type === \"JSXElement\") {\n\t\t\tconst openingElement = node.body.openingElement\n\t\t\tif (openingElement?.name?.type === \"JSXIdentifier\") {\n\t\t\t\treturn openingElement.name.name\n\t\t\t}\n\t\t\t// Handle JSXMemberExpression like <Namespace.Component />\n\t\t\tif (openingElement?.name?.type === \"JSXMemberExpression\") {\n\t\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Namespaced JSX component${loc}. Component extraction not fully supported for member expressions.`,\n\t\t\t\t)\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t}\n\t\t// Block body arrow functions: () => { return <Home /> }\n\t\tif (node.body.type === \"BlockStatement\") {\n\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Arrow function with block body${loc}. Only concise arrow functions returning JSX directly can be analyzed.`,\n\t\t\t)\n\t\t\treturn undefined\n\t\t}\n\t\t// JSX Fragment: () => <>...</>\n\t\tif (node.body.type === \"JSXFragment\") {\n\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`JSX Fragment detected${loc}. Cannot extract component name from fragments.`,\n\t\t\t)\n\t\t\treturn undefined\n\t\t}\n\t}\n\n\treturn undefined\n}\n\n/**\n * Extract import path from lazy function patterns\n */\nfunction extractLazyImportPath(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): string | undefined {\n\t// Arrow function: () => import('./path')\n\tif (node.type === \"ArrowFunctionExpression\") {\n\t\tconst body = node.body\n\n\t\t// Direct import: () => import('./path')\n\t\tif (body.type === \"CallExpression\" && body.callee.type === \"Import\") {\n\t\t\tif (body.arguments[0]?.type === \"StringLiteral\") {\n\t\t\t\treturn resolveImportPath(body.arguments[0].value, baseDir)\n\t\t\t}\n\t\t}\n\n\t\t// Chained: () => import('./path').then(d => d.Route)\n\t\tif (\n\t\t\tbody.type === \"CallExpression\" &&\n\t\t\tbody.callee.type === \"MemberExpression\" &&\n\t\t\tbody.callee.object?.type === \"CallExpression\" &&\n\t\t\tbody.callee.object.callee?.type === \"Import\"\n\t\t) {\n\t\t\tconst importCall = body.callee.object\n\t\t\tif (importCall.arguments[0]?.type === \"StringLiteral\") {\n\t\t\t\treturn resolveImportPath(importCall.arguments[0].value, baseDir)\n\t\t\t}\n\t\t}\n\t}\n\n\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\twarnings.push(\n\t\t`Unrecognized lazy pattern (${node.type})${loc}. Expected arrow function with import().`,\n\t)\n\treturn undefined\n}\n\n/**\n * Process addChildren calls to establish parent-child relationships\n */\nfunction processAddChildrenCalls(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\trouteMap: Map<string, RouteDefinition>,\n\twarnings: string[],\n): void {\n\t// Handle: const routeTree = rootRoute.addChildren([...])\n\t// or: const routeTree = rootRoute.addChildren([indexRoute, aboutRoute.addChildren([...])])\n\tif (node.type === \"VariableDeclaration\") {\n\t\tfor (const decl of node.declarations) {\n\t\t\tprocessAddChildrenExpression(decl.init, routeMap, warnings)\n\t\t}\n\t}\n\n\t// Handle: export const routeTree = rootRoute.addChildren([...])\n\tif (\n\t\tnode.type === \"ExportNamedDeclaration\" &&\n\t\tnode.declaration?.type === \"VariableDeclaration\"\n\t) {\n\t\tfor (const decl of node.declaration.declarations) {\n\t\t\tprocessAddChildrenExpression(decl.init, routeMap, warnings)\n\t\t}\n\t}\n}\n\n/**\n * Recursively process addChildren expressions\n */\nfunction processAddChildrenExpression(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\texpr: any,\n\trouteMap: Map<string, RouteDefinition>,\n\twarnings: string[],\n): string | undefined {\n\tif (!expr) return undefined\n\n\t// Handle: parentRoute.addChildren([...])\n\tif (\n\t\texpr.type === \"CallExpression\" &&\n\t\texpr.callee?.type === \"MemberExpression\" &&\n\t\texpr.callee.property?.type === \"Identifier\" &&\n\t\texpr.callee.property.name === \"addChildren\"\n\t) {\n\t\t// Get parent route variable name\n\t\tlet parentVarName: string | undefined\n\t\tif (expr.callee.object?.type === \"Identifier\") {\n\t\t\tparentVarName = expr.callee.object.name\n\t\t} else if (expr.callee.object?.type === \"CallExpression\") {\n\t\t\t// Nested addChildren: parentRoute.addChildren([...]).addChildren([...])\n\t\t\t// This is rare but handle recursively\n\t\t\tparentVarName = processAddChildrenExpression(\n\t\t\t\texpr.callee.object,\n\t\t\t\trouteMap,\n\t\t\t\twarnings,\n\t\t\t)\n\t\t}\n\n\t\tif (!parentVarName) return undefined\n\n\t\tconst parentDef = routeMap.get(parentVarName)\n\t\tif (!parentDef) {\n\t\t\tconst loc = expr.loc ? ` at line ${expr.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Parent route \"${parentVarName}\" not found${loc}. Ensure it's defined with createRoute/createRootRoute.`,\n\t\t\t)\n\t\t\treturn undefined\n\t\t}\n\n\t\t// Process children array\n\t\tconst childrenArg = expr.arguments[0]\n\t\tif (childrenArg?.type === \"ArrayExpression\") {\n\t\t\tconst childNames: string[] = []\n\n\t\t\tfor (const element of childrenArg.elements) {\n\t\t\t\tif (!element) continue\n\n\t\t\t\t// Direct reference: indexRoute\n\t\t\t\tif (element.type === \"Identifier\") {\n\t\t\t\t\tchildNames.push(element.name)\n\t\t\t\t}\n\t\t\t\t// Nested addChildren: aboutRoute.addChildren([...])\n\t\t\t\telse if (element.type === \"CallExpression\") {\n\t\t\t\t\tconst nestedParent = processAddChildrenExpression(\n\t\t\t\t\t\telement,\n\t\t\t\t\t\trouteMap,\n\t\t\t\t\t\twarnings,\n\t\t\t\t\t)\n\t\t\t\t\tif (nestedParent) {\n\t\t\t\t\t\tchildNames.push(nestedParent)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Spread operator\n\t\t\t\telse if (element.type === \"SpreadElement\") {\n\t\t\t\t\tconst loc = element.loc ? ` at line ${element.loc.start.line}` : \"\"\n\t\t\t\t\twarnings.push(\n\t\t\t\t\t\t`Spread operator detected${loc}. Routes from spread cannot be statically analyzed.`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tparentDef.children = childNames\n\t\t}\n\n\t\treturn parentVarName\n\t}\n\n\treturn undefined\n}\n\n/**\n * Build the route tree from collected definitions\n */\nfunction buildRouteTree(\n\trouteMap: Map<string, RouteDefinition>,\n\twarnings: string[],\n): ParsedRoute[] {\n\t// Find root routes\n\tconst rootDefs = Array.from(routeMap.values()).filter((def) => def.isRoot)\n\n\tif (rootDefs.length === 0) {\n\t\t// No explicit root route, try to build from parent relationships\n\t\treturn buildTreeFromParentRelations(routeMap, warnings)\n\t}\n\n\t// Build tree starting from root routes\n\tconst routes: ParsedRoute[] = []\n\n\tfor (const rootDef of rootDefs) {\n\t\t// Use a Set to track visited routes and detect circular references\n\t\tconst visited = new Set<string>()\n\t\tconst rootRoute = buildRouteFromDefinition(\n\t\t\trootDef,\n\t\t\trouteMap,\n\t\t\twarnings,\n\t\t\tvisited,\n\t\t)\n\t\tif (rootRoute) {\n\t\t\t// If root has children, return only the children (root is typically just a layout)\n\t\t\t// because the root route in TanStack Router serves as a layout wrapper\n\t\t\tif (rootRoute.children && rootRoute.children.length > 0) {\n\t\t\t\troutes.push(...rootRoute.children)\n\t\t\t} else if (rootRoute.path) {\n\t\t\t\troutes.push(rootRoute)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn routes\n}\n\n/**\n * Build tree when there's no explicit root route\n * Falls back to finding routes that have no parent relationship defined\n */\nfunction buildTreeFromParentRelations(\n\trouteMap: Map<string, RouteDefinition>,\n\twarnings: string[],\n): ParsedRoute[] {\n\t// Find routes without parents (top-level routes)\n\tconst topLevelDefs = Array.from(routeMap.values()).filter(\n\t\t(def) => !def.parentVariableName && !def.isRoot,\n\t)\n\n\tconst routes: ParsedRoute[] = []\n\n\tfor (const def of topLevelDefs) {\n\t\tconst visited = new Set<string>()\n\t\tconst route = buildRouteFromDefinition(def, routeMap, warnings, visited)\n\t\tif (route) {\n\t\t\troutes.push(route)\n\t\t}\n\t}\n\n\treturn routes\n}\n\n/**\n * Build a ParsedRoute from a RouteDefinition\n * @param visited - Set of visited variable names for circular reference detection\n */\nfunction buildRouteFromDefinition(\n\tdef: RouteDefinition,\n\trouteMap: Map<string, RouteDefinition>,\n\twarnings: string[],\n\tvisited: Set<string>,\n): ParsedRoute | null {\n\t// Circular reference detection\n\tif (visited.has(def.variableName)) {\n\t\twarnings.push(\n\t\t\t`Circular reference detected: route \"${def.variableName}\" references itself in the route tree.`,\n\t\t)\n\t\treturn null\n\t}\n\tvisited.add(def.variableName)\n\n\tconst route: ParsedRoute = {\n\t\tpath: def.path ?? \"\",\n\t\tcomponent: def.component,\n\t}\n\n\t// Process children\n\tif (def.children && def.children.length > 0) {\n\t\tconst children: ParsedRoute[] = []\n\t\tfor (const childName of def.children) {\n\t\t\tconst childDef = routeMap.get(childName)\n\t\t\tif (childDef) {\n\t\t\t\tconst childRoute = buildRouteFromDefinition(\n\t\t\t\t\tchildDef,\n\t\t\t\t\trouteMap,\n\t\t\t\t\twarnings,\n\t\t\t\t\tvisited,\n\t\t\t\t)\n\t\t\t\tif (childRoute) {\n\t\t\t\t\tchildren.push(childRoute)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Child route \"${childName}\" not found. Ensure it's defined with createRoute.`,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\tif (children.length > 0) {\n\t\t\troute.children = children\n\t\t}\n\t}\n\n\treturn route\n}\n\n/**\n * Normalize TanStack Router path syntax to standard format\n * /$ (trailing catch-all) -> /*\n * $ (standalone splat) -> *\n * $param (dynamic segment) -> :param\n */\nfunction normalizeTanStackPath(path: string): string {\n\treturn (\n\t\tpath\n\t\t\t// Convert catch-all: $ at end -> *\n\t\t\t.replace(/\\/\\$$/, \"/*\")\n\t\t\t// Convert single $ to * (splat route)\n\t\t\t.replace(/^\\$$/, \"*\")\n\t\t\t// Convert $param to :param\n\t\t\t.replace(/\\$([a-zA-Z_][a-zA-Z0-9_]*)/g, \":$1\")\n\t)\n}\n\n/**\n * Detect if content is TanStack Router based on patterns\n */\nexport function isTanStackRouterContent(content: string): boolean {\n\t// Check for TanStack Router specific imports\n\tif (content.includes(\"@tanstack/react-router\")) {\n\t\treturn true\n\t}\n\n\t// Check for createRootRoute pattern\n\tif (content.includes(\"createRootRoute\")) {\n\t\treturn true\n\t}\n\n\t// Check for createRoute with getParentRoute (TanStack Router specific)\n\tif (content.includes(\"createRoute\") && content.includes(\"getParentRoute\")) {\n\t\treturn true\n\t}\n\n\t// Check for lazyRouteComponent (TanStack Router specific)\n\tif (content.includes(\"lazyRouteComponent\")) {\n\t\treturn true\n\t}\n\n\t// Check for addChildren pattern (TanStack Router specific)\n\tif (/\\.addChildren\\s*\\(/.test(content)) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n","import { readFileSync } from \"node:fs\"\nimport { dirname, resolve } from \"node:path\"\nimport { parse } from \"@babel/parser\"\nimport { isAngularRouterContent } from \"./angularRouterParser.js\"\nimport {\n\ttype ParsedRoute,\n\ttype ParseResult,\n\ttype RouterType,\n\tresolveImportPath,\n} from \"./routeParserUtils.js\"\nimport { isSolidRouterContent } from \"./solidRouterParser.js\"\nimport { isTanStackRouterContent } from \"./tanstackRouterParser.js\"\n\n// Re-export shared types\nexport type { ParsedRoute, ParseResult, RouterType }\n// Re-export router detection for convenience\nexport { isAngularRouterContent, isSolidRouterContent, isTanStackRouterContent }\n\n/**\n * Router factory function names to detect\n */\nconst ROUTER_FACTORY_NAMES = [\n\t\"createBrowserRouter\",\n\t\"createHashRouter\",\n\t\"createMemoryRouter\",\n]\n\n/**\n * Parse React Router configuration file and extract routes\n */\nexport function parseReactRouterConfig(\n\tfilePath: string,\n\tpreloadedContent?: string,\n): ParseResult {\n\tconst absolutePath = resolve(filePath)\n\tconst routesFileDir = dirname(absolutePath)\n\tconst warnings: string[] = []\n\n\t// Read file with proper error handling (skip if content is preloaded)\n\tlet content: string\n\tif (preloadedContent !== undefined) {\n\t\tcontent = preloadedContent\n\t} else {\n\t\ttry {\n\t\t\tcontent = readFileSync(absolutePath, \"utf-8\")\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to read routes file \"${absolutePath}\": ${message}`,\n\t\t\t)\n\t\t}\n\t}\n\n\t// Parse with Babel - wrap for better error messages\n\tlet ast: ReturnType<typeof parse>\n\ttry {\n\t\tast = parse(content, {\n\t\t\tsourceType: \"module\",\n\t\t\tplugins: [\"typescript\", \"jsx\"],\n\t\t})\n\t} catch (error) {\n\t\tif (error instanceof SyntaxError) {\n\t\t\tthrow new Error(\n\t\t\t\t`Syntax error in routes file \"${absolutePath}\": ${error.message}`,\n\t\t\t)\n\t\t}\n\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\tthrow new Error(`Failed to parse routes file \"${absolutePath}\": ${message}`)\n\t}\n\n\tconst routes: ParsedRoute[] = []\n\n\t// Find routes array in the AST\n\tfor (const node of ast.program.body) {\n\t\t// Handle: const router = createBrowserRouter([...])\n\t\tif (node.type === \"VariableDeclaration\") {\n\t\t\tfor (const decl of node.declarations) {\n\t\t\t\tif (decl.init?.type === \"CallExpression\") {\n\t\t\t\t\tconst callee = decl.init.callee\n\t\t\t\t\tif (\n\t\t\t\t\t\tcallee.type === \"Identifier\" &&\n\t\t\t\t\t\tROUTER_FACTORY_NAMES.includes(callee.name)\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst firstArg = decl.init.arguments[0]\n\t\t\t\t\t\tif (firstArg?.type === \"ArrayExpression\") {\n\t\t\t\t\t\t\tconst parsed = parseRoutesArray(firstArg, routesFileDir, warnings)\n\t\t\t\t\t\t\troutes.push(...parsed)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle: export const router = createBrowserRouter([...])\n\t\t// and: export const routes = [...]\n\t\tif (\n\t\t\tnode.type === \"ExportNamedDeclaration\" &&\n\t\t\tnode.declaration?.type === \"VariableDeclaration\"\n\t\t) {\n\t\t\tfor (const decl of node.declaration.declarations) {\n\t\t\t\t// Handle: export const router = createBrowserRouter([...])\n\t\t\t\tif (decl.init?.type === \"CallExpression\") {\n\t\t\t\t\tconst callee = decl.init.callee\n\t\t\t\t\tif (\n\t\t\t\t\t\tcallee.type === \"Identifier\" &&\n\t\t\t\t\t\tROUTER_FACTORY_NAMES.includes(callee.name)\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst firstArg = decl.init.arguments[0]\n\t\t\t\t\t\tif (firstArg?.type === \"ArrayExpression\") {\n\t\t\t\t\t\t\tconst parsed = parseRoutesArray(firstArg, routesFileDir, warnings)\n\t\t\t\t\t\t\troutes.push(...parsed)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Handle: export const routes = [...]\n\t\t\t\tif (\n\t\t\t\t\tdecl.id.type === \"Identifier\" &&\n\t\t\t\t\tdecl.id.name === \"routes\" &&\n\t\t\t\t\tdecl.init?.type === \"ArrayExpression\"\n\t\t\t\t) {\n\t\t\t\t\tconst parsed = parseRoutesArray(decl.init, routesFileDir, warnings)\n\t\t\t\t\troutes.push(...parsed)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle: const routes = [...]; (for later export)\n\t\tif (node.type === \"VariableDeclaration\") {\n\t\t\tfor (const decl of node.declarations) {\n\t\t\t\tif (\n\t\t\t\t\tdecl.id.type === \"Identifier\" &&\n\t\t\t\t\tdecl.id.name === \"routes\" &&\n\t\t\t\t\tdecl.init?.type === \"ArrayExpression\"\n\t\t\t\t) {\n\t\t\t\t\tconst parsed = parseRoutesArray(decl.init, routesFileDir, warnings)\n\t\t\t\t\troutes.push(...parsed)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle: export default [...]\n\t\tif (\n\t\t\tnode.type === \"ExportDefaultDeclaration\" &&\n\t\t\tnode.declaration.type === \"ArrayExpression\"\n\t\t) {\n\t\t\tconst parsed = parseRoutesArray(node.declaration, routesFileDir, warnings)\n\t\t\troutes.push(...parsed)\n\t\t}\n\n\t\t// Handle: export default [...] satisfies RouteObject[]\n\t\tif (\n\t\t\tnode.type === \"ExportDefaultDeclaration\" &&\n\t\t\tnode.declaration.type === \"TSSatisfiesExpression\" &&\n\t\t\tnode.declaration.expression.type === \"ArrayExpression\"\n\t\t) {\n\t\t\tconst parsed = parseRoutesArray(\n\t\t\t\tnode.declaration.expression,\n\t\t\t\troutesFileDir,\n\t\t\t\twarnings,\n\t\t\t)\n\t\t\troutes.push(...parsed)\n\t\t}\n\t}\n\n\t// Warn if no routes were found\n\tif (routes.length === 0) {\n\t\twarnings.push(\n\t\t\t\"No routes found. Supported patterns: 'createBrowserRouter([...])', 'export const routes = [...]', or 'export default [...]'\",\n\t\t)\n\t}\n\n\treturn { routes, warnings }\n}\n\n/**\n * Parse an array expression containing route objects\n */\nfunction parseRoutesArray(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tarrayNode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): ParsedRoute[] {\n\tconst routes: ParsedRoute[] = []\n\n\tfor (const element of arrayNode.elements) {\n\t\tif (!element) continue\n\n\t\t// Handle spread elements\n\t\tif (element.type === \"SpreadElement\") {\n\t\t\tconst loc = element.loc ? ` at line ${element.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Spread operator detected${loc}. Routes from spread cannot be statically analyzed.`,\n\t\t\t)\n\t\t\tcontinue\n\t\t}\n\n\t\tif (element.type === \"ObjectExpression\") {\n\t\t\tconst route = parseRouteObject(element, baseDir, warnings)\n\t\t\tif (route) {\n\t\t\t\troutes.push(route)\n\t\t\t}\n\t\t} else {\n\t\t\tconst loc = element.loc ? ` at line ${element.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Non-object route element (${element.type})${loc}. Only object literals can be statically analyzed.`,\n\t\t\t)\n\t\t}\n\t}\n\n\treturn routes\n}\n\n/**\n * Parse a single route object expression\n */\nfunction parseRouteObject(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tobjectNode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): ParsedRoute | null {\n\tconst route: ParsedRoute = {\n\t\tpath: \"\",\n\t}\n\tlet isIndexRoute = false\n\tlet hasPath = false\n\n\tfor (const prop of objectNode.properties) {\n\t\tif (prop.type !== \"ObjectProperty\") continue\n\t\tif (prop.key.type !== \"Identifier\") continue\n\n\t\tconst key = prop.key.name\n\n\t\tswitch (key) {\n\t\t\tcase \"path\":\n\t\t\t\tif (prop.value.type === \"StringLiteral\") {\n\t\t\t\t\troute.path = prop.value.value\n\t\t\t\t\thasPath = true\n\t\t\t\t} else {\n\t\t\t\t\tconst loc = prop.loc ? ` at line ${prop.loc.start.line}` : \"\"\n\t\t\t\t\twarnings.push(\n\t\t\t\t\t\t`Dynamic path value (${prop.value.type})${loc}. Only string literal paths can be statically analyzed.`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\tcase \"index\":\n\t\t\t\tif (prop.value.type === \"BooleanLiteral\" && prop.value.value === true) {\n\t\t\t\t\tisIndexRoute = true\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\tcase \"element\":\n\t\t\t\troute.component = extractComponentFromJSX(prop.value, warnings)\n\t\t\t\tbreak\n\n\t\t\tcase \"Component\":\n\t\t\t\tif (prop.value.type === \"Identifier\") {\n\t\t\t\t\troute.component = prop.value.name\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\tcase \"lazy\": {\n\t\t\t\tconst lazyPath = extractLazyImportPath(prop.value, baseDir, warnings)\n\t\t\t\tif (lazyPath) {\n\t\t\t\t\troute.component = lazyPath\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tcase \"children\":\n\t\t\t\tif (prop.value.type === \"ArrayExpression\") {\n\t\t\t\t\troute.children = parseRoutesArray(prop.value, baseDir, warnings)\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t}\n\t}\n\n\t// Handle index routes\n\tif (isIndexRoute) {\n\t\troute.path = \"\"\n\t\treturn route\n\t}\n\n\t// Skip routes without path (layout wrappers without path)\n\t// These are parent routes that only serve as layout containers\n\tif (!hasPath && !isIndexRoute) {\n\t\t// If it has children, process them with empty parent path contribution\n\t\tif (route.children && route.children.length > 0) {\n\t\t\t// Return a route with empty path to act as layout\n\t\t\troute.path = \"\"\n\t\t\treturn route\n\t\t}\n\t\treturn null\n\t}\n\n\treturn route\n}\n\n/**\n * Extract component name from JSX element\n * element: <Dashboard /> -> \"Dashboard\"\n */\nfunction extractComponentFromJSX(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\twarnings: string[],\n): string | undefined {\n\tif (node.type === \"JSXElement\") {\n\t\tconst openingElement = node.openingElement\n\t\tif (openingElement?.name?.type === \"JSXIdentifier\") {\n\t\t\treturn openingElement.name.name\n\t\t}\n\t\tif (openingElement?.name?.type === \"JSXMemberExpression\") {\n\t\t\t// Handle <Namespace.Component />\n\t\t\tconst parts: string[] = []\n\t\t\tlet current = openingElement.name\n\t\t\twhile (current) {\n\t\t\t\tif (current.type === \"JSXIdentifier\") {\n\t\t\t\t\tparts.unshift(current.name)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tif (current.type === \"JSXMemberExpression\") {\n\t\t\t\t\tif (current.property?.type === \"JSXIdentifier\") {\n\t\t\t\t\t\tparts.unshift(current.property.name)\n\t\t\t\t\t}\n\t\t\t\t\tcurrent = current.object\n\t\t\t\t} else {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn parts.join(\".\")\n\t\t}\n\n\t\t// Check if it has children (wrapper component)\n\t\tif (\n\t\t\tnode.children &&\n\t\t\tnode.children.length > 0 &&\n\t\t\topeningElement?.name?.type === \"JSXIdentifier\"\n\t\t) {\n\t\t\tconst wrapperName = openingElement.name.name\n\t\t\t// Find the first JSX child\n\t\t\tfor (const child of node.children) {\n\t\t\t\tif (child.type === \"JSXElement\") {\n\t\t\t\t\tconst childComponent = extractComponentFromJSX(child, warnings)\n\t\t\t\t\tif (childComponent) {\n\t\t\t\t\t\twarnings.push(\n\t\t\t\t\t\t\t`Wrapper component detected: <${wrapperName}>. Using wrapper name for screen.`,\n\t\t\t\t\t\t)\n\t\t\t\t\t\treturn wrapperName\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Handle JSXFragment\n\tif (node.type === \"JSXFragment\") {\n\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\twarnings.push(\n\t\t\t`JSX Fragment detected${loc}. Cannot extract component name from fragments. Consider wrapping in a named component.`,\n\t\t)\n\t\treturn undefined\n\t}\n\n\t// Catch-all for unrecognized element patterns\n\tif (node) {\n\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\twarnings.push(\n\t\t\t`Unrecognized element pattern (${node.type})${loc}. Component will not be extracted.`,\n\t\t)\n\t}\n\treturn undefined\n}\n\n/**\n * Extract import path from lazy function\n * lazy: () => import(\"./pages/Dashboard\") -> resolved path\n */\nfunction extractLazyImportPath(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tnode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): string | undefined {\n\t// Arrow function: () => import('./path')\n\tif (node.type === \"ArrowFunctionExpression\") {\n\t\tconst body = node.body\n\n\t\tif (body.type === \"CallExpression\" && body.callee.type === \"Import\") {\n\t\t\tif (body.arguments[0]?.type === \"StringLiteral\") {\n\t\t\t\treturn resolveImportPath(body.arguments[0].value, baseDir)\n\t\t\t}\n\t\t\t// Dynamic import argument\n\t\t\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Lazy import with dynamic path${loc}. Only string literal imports can be statically analyzed.`,\n\t\t\t)\n\t\t\treturn undefined\n\t\t}\n\t}\n\n\t// Unrecognized lazy pattern\n\tconst loc = node.loc ? ` at line ${node.loc.start.line}` : \"\"\n\twarnings.push(\n\t\t`Unrecognized lazy pattern (${node.type})${loc}. Expected arrow function with import().`,\n\t)\n\treturn undefined\n}\n\n/**\n * Detect if content is React Router based on patterns\n */\nexport function isReactRouterContent(content: string): boolean {\n\t// Check for React Router specific patterns\n\tif (\n\t\tcontent.includes(\"createBrowserRouter\") ||\n\t\tcontent.includes(\"createHashRouter\") ||\n\t\tcontent.includes(\"createMemoryRouter\") ||\n\t\tcontent.includes(\"RouteObject\")\n\t) {\n\t\treturn true\n\t}\n\n\t// Check for JSX element pattern in routes\n\tif (/element:\\s*</.test(content)) {\n\t\treturn true\n\t}\n\n\t// Check for Component property pattern (uppercase)\n\tif (/Component:\\s*[A-Z]/.test(content)) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n/**\n * Detect if content is Vue Router based on patterns\n */\nexport function isVueRouterContent(content: string): boolean {\n\t// Check for Vue Router specific patterns\n\tif (\n\t\tcontent.includes(\"RouteRecordRaw\") ||\n\t\tcontent.includes(\"vue-router\") ||\n\t\tcontent.includes(\".vue\")\n\t) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n/**\n * Detect router type from file content.\n * Detection order: TanStack Router -> Solid Router -> Angular Router -> React Router -> Vue Router.\n * This order ensures more specific patterns are checked before more generic ones.\n */\nexport function detectRouterType(content: string): RouterType {\n\t// Check TanStack Router first (more specific patterns)\n\tif (isTanStackRouterContent(content)) {\n\t\treturn \"tanstack-router\"\n\t}\n\t// Check Solid Router before React Router (both use similar patterns)\n\tif (isSolidRouterContent(content)) {\n\t\treturn \"solid-router\"\n\t}\n\t// Check Angular Router before React Router (distinct patterns)\n\tif (isAngularRouterContent(content)) {\n\t\treturn \"angular-router\"\n\t}\n\tif (isReactRouterContent(content)) {\n\t\treturn \"react-router\"\n\t}\n\tif (isVueRouterContent(content)) {\n\t\treturn \"vue-router\"\n\t}\n\treturn \"unknown\"\n}\n","import { readFileSync } from \"node:fs\"\nimport { dirname, resolve } from \"node:path\"\nimport { parse } from \"@babel/parser\"\nimport {\n\ttype FlatRoute,\n\tflattenRoutes,\n\ttype ParsedRoute,\n\ttype ParseResult,\n\tpathToScreenId,\n\tpathToScreenTitle,\n\tresolveImportPath,\n} from \"./routeParserUtils.js\"\n\n// Re-export shared types and utilities\nexport type { ParsedRoute, FlatRoute, ParseResult }\nexport { flattenRoutes, pathToScreenId, pathToScreenTitle }\n\n/**\n * Parse Vue Router configuration file and extract routes\n */\nexport function parseVueRouterConfig(\n\tfilePath: string,\n\tpreloadedContent?: string,\n): ParseResult {\n\tconst absolutePath = resolve(filePath)\n\tconst routesFileDir = dirname(absolutePath)\n\tconst warnings: string[] = []\n\n\t// Read file with proper error handling (skip if content is preloaded)\n\tlet content: string\n\tif (preloadedContent !== undefined) {\n\t\tcontent = preloadedContent\n\t} else {\n\t\ttry {\n\t\t\tcontent = readFileSync(absolutePath, \"utf-8\")\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to read routes file \"${absolutePath}\": ${message}`,\n\t\t\t)\n\t\t}\n\t}\n\n\t// Parse with Babel - wrap for better error messages\n\tlet ast: ReturnType<typeof parse>\n\ttry {\n\t\tast = parse(content, {\n\t\t\tsourceType: \"module\",\n\t\t\tplugins: [\"typescript\", \"jsx\"],\n\t\t})\n\t} catch (error) {\n\t\tif (error instanceof SyntaxError) {\n\t\t\tthrow new Error(\n\t\t\t\t`Syntax error in routes file \"${absolutePath}\": ${error.message}`,\n\t\t\t)\n\t\t}\n\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\tthrow new Error(`Failed to parse routes file \"${absolutePath}\": ${message}`)\n\t}\n\n\tconst routes: ParsedRoute[] = []\n\n\t// Find routes array in the AST\n\tfor (const node of ast.program.body) {\n\t\t// Handle: export const routes = [...]\n\t\tif (\n\t\t\tnode.type === \"ExportNamedDeclaration\" &&\n\t\t\tnode.declaration?.type === \"VariableDeclaration\"\n\t\t) {\n\t\t\tfor (const decl of node.declaration.declarations) {\n\t\t\t\tif (\n\t\t\t\t\tdecl.id.type === \"Identifier\" &&\n\t\t\t\t\tdecl.id.name === \"routes\" &&\n\t\t\t\t\tdecl.init?.type === \"ArrayExpression\"\n\t\t\t\t) {\n\t\t\t\t\tconst parsed = parseRoutesArray(decl.init, routesFileDir, warnings)\n\t\t\t\t\troutes.push(...parsed)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle: const routes = [...]; export { routes }\n\t\tif (node.type === \"VariableDeclaration\") {\n\t\t\tfor (const decl of node.declarations) {\n\t\t\t\tif (\n\t\t\t\t\tdecl.id.type === \"Identifier\" &&\n\t\t\t\t\tdecl.id.name === \"routes\" &&\n\t\t\t\t\tdecl.init?.type === \"ArrayExpression\"\n\t\t\t\t) {\n\t\t\t\t\tconst parsed = parseRoutesArray(decl.init, routesFileDir, warnings)\n\t\t\t\t\troutes.push(...parsed)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle: export default [...]\n\t\tif (\n\t\t\tnode.type === \"ExportDefaultDeclaration\" &&\n\t\t\tnode.declaration.type === \"ArrayExpression\"\n\t\t) {\n\t\t\tconst parsed = parseRoutesArray(node.declaration, routesFileDir, warnings)\n\t\t\troutes.push(...parsed)\n\t\t}\n\n\t\t// Handle: export default [...] satisfies RouteRecordRaw[]\n\t\tif (\n\t\t\tnode.type === \"ExportDefaultDeclaration\" &&\n\t\t\tnode.declaration.type === \"TSSatisfiesExpression\" &&\n\t\t\tnode.declaration.expression.type === \"ArrayExpression\"\n\t\t) {\n\t\t\tconst parsed = parseRoutesArray(\n\t\t\t\tnode.declaration.expression,\n\t\t\t\troutesFileDir,\n\t\t\t\twarnings,\n\t\t\t)\n\t\t\troutes.push(...parsed)\n\t\t}\n\t}\n\n\t// Warn if no routes were found\n\tif (routes.length === 0) {\n\t\twarnings.push(\n\t\t\t\"No routes array found. Supported patterns: 'export const routes = [...]', 'export default [...]', or 'export default [...] satisfies RouteRecordRaw[]'\",\n\t\t)\n\t}\n\n\treturn { routes, warnings }\n}\n\n/**\n * Parse an array expression containing route objects\n */\nfunction parseRoutesArray(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tarrayNode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): ParsedRoute[] {\n\tconst routes: ParsedRoute[] = []\n\n\tfor (const element of arrayNode.elements) {\n\t\tif (!element) continue\n\n\t\t// Handle spread elements\n\t\tif (element.type === \"SpreadElement\") {\n\t\t\tconst loc = element.loc ? ` at line ${element.loc.start.line}` : \"\"\n\t\t\twarnings.push(\n\t\t\t\t`Spread operator detected${loc}. Routes from spread cannot be statically analyzed.`,\n\t\t\t)\n\t\t\tcontinue\n\t\t}\n\n\t\tif (element.type === \"ObjectExpression\") {\n\t\t\tconst route = parseRouteObject(element, baseDir, warnings)\n\t\t\tif (route) {\n\t\t\t\troutes.push(route)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn routes\n}\n\n/**\n * Parse a single route object expression\n */\nfunction parseRouteObject(\n\t// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\n\tobjectNode: any,\n\tbaseDir: string,\n\twarnings: string[],\n): ParsedRoute | null {\n\tconst route: ParsedRoute = {\n\t\tpath: \"\",\n\t}\n\n\tfor (const prop of objectNode.properties) {\n\t\tif (prop.type !== \"ObjectProperty\") continue\n\t\tif (prop.key.type !== \"Identifier\") continue\n\n\t\tconst key = prop.key.name\n\n\t\tswitch (key) {\n\t\t\tcase \"path\":\n\t\t\t\tif (prop.value.type === \"StringLiteral\") {\n\t\t\t\t\troute.path = prop.value.value\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\tcase \"name\":\n\t\t\t\tif (prop.value.type === \"StringLiteral\") {\n\t\t\t\t\troute.name = prop.value.value\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\tcase \"redirect\":\n\t\t\t\tif (prop.value.type === \"StringLiteral\") {\n\t\t\t\t\troute.redirect = prop.value.value\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\tcase \"component\":\n\t\t\t\troute.component = extractComponentPath(prop.value, baseDir)\n\t\t\t\tbreak\n\n\t\t\tcase \"children\":\n\t\t\t\tif (prop.value.type === \"ArrayExpression\") {\n\t\t\t\t\troute.children = parseRoutesArray(prop.value, baseDir, warnings)\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t}\n\t}\n\n\t// Skip routes without path\n\tif (!route.path) {\n\t\treturn null\n\t}\n\n\treturn route\n}\n\n/**\n * Extract component path from various component definitions\n */\n// biome-ignore lint/suspicious/noExplicitAny: AST node types are complex\nfunction extractComponentPath(node: any, baseDir: string): string | undefined {\n\t// Direct identifier: component: HomeView\n\tif (node.type === \"Identifier\") {\n\t\treturn undefined // Can't resolve without tracking imports\n\t}\n\n\t// Arrow function with import: () => import('./views/Home.vue')\n\tif (node.type === \"ArrowFunctionExpression\") {\n\t\tconst body = node.body\n\n\t\t// () => import('./path')\n\t\tif (body.type === \"CallExpression\" && body.callee.type === \"Import\") {\n\t\t\tif (body.arguments[0]?.type === \"StringLiteral\") {\n\t\t\t\treturn resolveImportPath(body.arguments[0].value, baseDir)\n\t\t\t}\n\t\t}\n\n\t\t// () => import(/* webpackChunkName */ './path')\n\t\tif (body.type === \"CallExpression\" && body.callee.type === \"Import\") {\n\t\t\tfor (const arg of body.arguments) {\n\t\t\t\tif (arg.type === \"StringLiteral\") {\n\t\t\t\t\treturn resolveImportPath(arg.value, baseDir)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn undefined\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\"\nimport { dirname, join, relative, resolve } from \"node:path\"\nimport { define } from \"gunshi\"\nimport prompts from \"prompts\"\nimport { glob } from \"tinyglobby\"\nimport { parseAngularRouterConfig } from \"../utils/angularRouterParser.js\"\nimport { loadConfig } from \"../utils/config.js\"\nimport { ERRORS } from \"../utils/errors.js\"\nimport { logger } from \"../utils/logger.js\"\nimport {\n\tdetectRouterType,\n\tparseReactRouterConfig,\n} from \"../utils/reactRouterParser.js\"\nimport {\n\ttype FlatRoute,\n\tflattenRoutes,\n\ttype ParseResult,\n} from \"../utils/routeParserUtils.js\"\nimport { parseSolidRouterConfig } from \"../utils/solidRouterParser.js\"\nimport { parseTanStackRouterConfig } from \"../utils/tanstackRouterParser.js\"\nimport { parseVueRouterConfig } from \"../utils/vueRouterParser.js\"\n\nexport const generateCommand = define({\n\tname: \"generate\",\n\tdescription: \"Auto-generate screen.meta.ts files from route files\",\n\targs: {\n\t\tconfig: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"c\",\n\t\t\tdescription: \"Path to config file\",\n\t\t},\n\t\tdryRun: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"n\",\n\t\t\tdescription: \"Show what would be generated without writing files\",\n\t\t\tdefault: false,\n\t\t},\n\t\tforce: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"f\",\n\t\t\tdescription: \"Overwrite existing screen.meta.ts files\",\n\t\t\tdefault: false,\n\t\t},\n\t\tinteractive: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"i\",\n\t\t\tdescription: \"Interactively confirm or modify each screen\",\n\t\t\tdefault: false,\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tconst config = await loadConfig(ctx.values.config)\n\t\tconst cwd = process.cwd()\n\t\tconst dryRun = ctx.values.dryRun ?? false\n\t\tconst force = ctx.values.force ?? false\n\t\tconst interactive = ctx.values.interactive ?? false\n\n\t\t// Check for routes configuration\n\t\tif (!config.routesPattern && !config.routesFile) {\n\t\t\tlogger.errorWithHelp(ERRORS.ROUTES_PATTERN_MISSING)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\t// Use routesFile mode (config-based routing)\n\t\tif (config.routesFile) {\n\t\t\tawait generateFromRoutesFile(config.routesFile, cwd, {\n\t\t\t\tdryRun,\n\t\t\t\tforce,\n\t\t\t\tinteractive,\n\t\t\t})\n\t\t\treturn\n\t\t}\n\n\t\t// Use routesPattern mode (file-based routing)\n\t\tawait generateFromRoutesPattern(config.routesPattern as string, cwd, {\n\t\t\tdryRun,\n\t\t\tforce,\n\t\t\tinteractive,\n\t\t\tignore: config.ignore,\n\t\t})\n\t},\n})\n\ninterface GenerateFromRoutesFileOptions {\n\tdryRun: boolean\n\tforce: boolean\n\tinteractive: boolean\n}\n\n/**\n * Generate screen.meta.ts files from a router config file (Vue Router or React Router)\n */\nasync function generateFromRoutesFile(\n\troutesFile: string,\n\tcwd: string,\n\toptions: GenerateFromRoutesFileOptions,\n): Promise<void> {\n\tconst { dryRun, force, interactive } = options\n\tconst absoluteRoutesFile = resolve(cwd, routesFile)\n\n\t// Check if routes file exists\n\tif (!existsSync(absoluteRoutesFile)) {\n\t\tlogger.errorWithHelp(ERRORS.ROUTES_FILE_NOT_FOUND(routesFile))\n\t\tprocess.exit(1)\n\t}\n\n\t// Detect router type\n\tconst content = readFileSync(absoluteRoutesFile, \"utf-8\")\n\tconst routerType = detectRouterType(content)\n\n\tconst routerTypeDisplay =\n\t\trouterType === \"tanstack-router\"\n\t\t\t? \"TanStack Router\"\n\t\t\t: routerType === \"solid-router\"\n\t\t\t\t? \"Solid Router\"\n\t\t\t\t: routerType === \"angular-router\"\n\t\t\t\t\t? \"Angular Router\"\n\t\t\t\t\t: routerType === \"react-router\"\n\t\t\t\t\t\t? \"React Router\"\n\t\t\t\t\t\t: routerType === \"vue-router\"\n\t\t\t\t\t\t\t? \"Vue Router\"\n\t\t\t\t\t\t\t: \"unknown\"\n\n\tlogger.info(\n\t\t`Parsing routes from ${logger.path(routesFile)} (${routerTypeDisplay})...`,\n\t)\n\tlogger.blank()\n\n\t// Parse the routes file with the appropriate parser\n\tlet parseResult: ParseResult\n\ttry {\n\t\tif (routerType === \"tanstack-router\") {\n\t\t\tparseResult = parseTanStackRouterConfig(absoluteRoutesFile)\n\t\t} else if (routerType === \"solid-router\") {\n\t\t\tparseResult = parseSolidRouterConfig(absoluteRoutesFile)\n\t\t} else if (routerType === \"angular-router\") {\n\t\t\tparseResult = parseAngularRouterConfig(absoluteRoutesFile)\n\t\t} else if (routerType === \"react-router\") {\n\t\t\tparseResult = parseReactRouterConfig(absoluteRoutesFile)\n\t\t} else if (routerType === \"vue-router\") {\n\t\t\tparseResult = parseVueRouterConfig(absoluteRoutesFile)\n\t\t} else {\n\t\t\t// Unknown router type - warn user and attempt Vue Router parser as fallback\n\t\t\tlogger.warn(\n\t\t\t\t`Could not auto-detect router type for ${logger.path(routesFile)}. Attempting to parse as Vue Router.`,\n\t\t\t)\n\t\t\tlogger.log(\n\t\t\t\t` ${logger.dim(\"If parsing fails, check that your router imports are explicit.\")}`,\n\t\t\t)\n\t\t\tparseResult = parseVueRouterConfig(absoluteRoutesFile)\n\t\t}\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\tlogger.errorWithHelp(ERRORS.ROUTES_FILE_PARSE_ERROR(routesFile, message))\n\t\tprocess.exit(1)\n\t}\n\n\t// Show warnings\n\tfor (const warning of parseResult.warnings) {\n\t\tlogger.warn(warning)\n\t}\n\n\t// Flatten routes\n\tconst flatRoutes = flattenRoutes(parseResult.routes)\n\n\tif (flatRoutes.length === 0) {\n\t\tlogger.warn(\"No routes found in the config file\")\n\t\treturn\n\t}\n\n\tlogger.log(`Found ${flatRoutes.length} routes`)\n\tlogger.blank()\n\n\tlet created = 0\n\tlet skipped = 0\n\n\tfor (const route of flatRoutes) {\n\t\t// Determine where to place screen.meta.ts\n\t\tconst metaPath = determineMetaPath(route, cwd)\n\t\tconst absoluteMetaPath = resolve(cwd, metaPath)\n\n\t\tif (!force && existsSync(absoluteMetaPath)) {\n\t\t\tif (!interactive) {\n\t\t\t\tskipped++\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tlogger.itemWarn(\n\t\t\t\t`Exists: ${logger.path(metaPath)} (use --force to overwrite)`,\n\t\t\t)\n\t\t\tskipped++\n\t\t\tcontinue\n\t\t}\n\n\t\tconst screenMeta: InferredScreenMeta = {\n\t\t\tid: route.screenId,\n\t\t\ttitle: route.screenTitle,\n\t\t\troute: route.fullPath,\n\t\t}\n\n\t\tif (interactive) {\n\t\t\tconst result = await promptForScreen(route.fullPath, screenMeta)\n\n\t\t\tif (result.skip) {\n\t\t\t\tlogger.itemWarn(`Skipped: ${logger.path(metaPath)}`)\n\t\t\t\tskipped++\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tconst content = generateScreenMetaContent(result.meta, {\n\t\t\t\towner: result.owner,\n\t\t\t\ttags: result.tags,\n\t\t\t})\n\n\t\t\tif (dryRun) {\n\t\t\t\tlogDryRunOutput(metaPath, result.meta, result.owner, result.tags)\n\t\t\t\tcreated++\n\t\t\t} else if (safeWriteFile(absoluteMetaPath, metaPath, content)) {\n\t\t\t\tcreated++\n\t\t\t}\n\t\t} else {\n\t\t\tconst content = generateScreenMetaContent(screenMeta)\n\n\t\t\tif (dryRun) {\n\t\t\t\tlogDryRunOutput(metaPath, screenMeta)\n\t\t\t\tcreated++\n\t\t\t} else if (safeWriteFile(absoluteMetaPath, metaPath, content)) {\n\t\t\t\tcreated++\n\t\t\t}\n\t\t}\n\t}\n\n\tlogSummary(created, skipped, dryRun)\n}\n\ninterface GenerateFromRoutesPatternOptions {\n\tdryRun: boolean\n\tforce: boolean\n\tinteractive: boolean\n\tignore: string[]\n}\n\n/**\n * Generate screen.meta.ts files from route files matching a glob pattern\n */\nasync function generateFromRoutesPattern(\n\troutesPattern: string,\n\tcwd: string,\n\toptions: GenerateFromRoutesPatternOptions,\n): Promise<void> {\n\tconst { dryRun, force, interactive, ignore } = options\n\n\tlogger.info(\"Scanning for route files...\")\n\tlogger.blank()\n\n\t// Find all route files\n\tconst routeFiles = await glob(routesPattern, {\n\t\tcwd,\n\t\tignore,\n\t})\n\n\tif (routeFiles.length === 0) {\n\t\tlogger.warn(`No route files found matching: ${routesPattern}`)\n\t\treturn\n\t}\n\n\tlogger.log(`Found ${routeFiles.length} route files`)\n\tlogger.blank()\n\n\tlet created = 0\n\tlet skipped = 0\n\n\tfor (const routeFile of routeFiles) {\n\t\tconst routeDir = dirname(routeFile)\n\t\tconst metaPath = join(routeDir, \"screen.meta.ts\")\n\t\tconst absoluteMetaPath = join(cwd, metaPath)\n\n\t\tif (!force && existsSync(absoluteMetaPath)) {\n\t\t\tif (!interactive) {\n\t\t\t\tskipped++\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tlogger.itemWarn(\n\t\t\t\t`Exists: ${logger.path(metaPath)} (use --force to overwrite)`,\n\t\t\t)\n\t\t\tskipped++\n\t\t\tcontinue\n\t\t}\n\n\t\t// Generate screen metadata from path\n\t\tconst screenMeta = inferScreenMeta(routeDir, routesPattern)\n\n\t\tif (interactive) {\n\t\t\tconst result = await promptForScreen(routeFile, screenMeta)\n\n\t\t\tif (result.skip) {\n\t\t\t\tlogger.itemWarn(`Skipped: ${logger.path(metaPath)}`)\n\t\t\t\tskipped++\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tconst content = generateScreenMetaContent(result.meta, {\n\t\t\t\towner: result.owner,\n\t\t\t\ttags: result.tags,\n\t\t\t})\n\n\t\t\tif (dryRun) {\n\t\t\t\tlogDryRunOutput(metaPath, result.meta, result.owner, result.tags)\n\t\t\t\tcreated++\n\t\t\t} else if (safeWriteFile(absoluteMetaPath, metaPath, content)) {\n\t\t\t\tcreated++\n\t\t\t}\n\t\t} else {\n\t\t\tconst content = generateScreenMetaContent(screenMeta)\n\n\t\t\tif (dryRun) {\n\t\t\t\tlogDryRunOutput(metaPath, screenMeta)\n\t\t\t\tcreated++\n\t\t\t} else if (safeWriteFile(absoluteMetaPath, metaPath, content)) {\n\t\t\t\tcreated++\n\t\t\t}\n\t\t}\n\t}\n\n\tlogSummary(created, skipped, dryRun)\n}\n\n/**\n * Determine where to place screen.meta.ts for a route\n */\nfunction determineMetaPath(route: FlatRoute, cwd: string): string {\n\t// If component path is available, place screen.meta.ts next to it\n\tif (route.componentPath) {\n\t\tconst componentDir = dirname(route.componentPath)\n\t\tconst relativePath = relative(cwd, componentDir)\n\t\t// Ensure path doesn't escape cwd\n\t\tif (!relativePath.startsWith(\"..\")) {\n\t\t\treturn join(relativePath, \"screen.meta.ts\")\n\t\t}\n\t}\n\n\t// Fall back to src/screens/{screenId}/screen.meta.ts\n\tconst screenDir = route.screenId.replace(/\\./g, \"/\")\n\treturn join(\"src\", \"screens\", screenDir, \"screen.meta.ts\")\n}\n\n/**\n * Ensure the directory for a file exists\n */\nfunction ensureDirectoryExists(filePath: string): void {\n\tconst dir = dirname(filePath)\n\tif (!existsSync(dir)) {\n\t\ttry {\n\t\t\tmkdirSync(dir, { recursive: true })\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\tthrow new Error(`Failed to create directory \"${dir}\": ${message}`)\n\t\t}\n\t}\n}\n\n/**\n * Safely write a file with error handling\n * Returns true if successful, false if failed\n */\nfunction safeWriteFile(\n\tabsolutePath: string,\n\trelativePath: string,\n\tcontent: string,\n): boolean {\n\ttry {\n\t\tensureDirectoryExists(absolutePath)\n\t\twriteFileSync(absolutePath, content)\n\t\tlogger.itemSuccess(`Created: ${logger.path(relativePath)}`)\n\t\treturn true\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\tlogger.itemError(\n\t\t\t`Failed to create ${logger.path(relativePath)}: ${message}`,\n\t\t)\n\t\treturn false\n\t}\n}\n\n/**\n * Log dry run output for a screen\n */\nfunction logDryRunOutput(\n\tmetaPath: string,\n\tmeta: InferredScreenMeta,\n\towner?: string[],\n\ttags?: string[],\n): void {\n\tlogger.step(`Would create: ${logger.path(metaPath)}`)\n\tlogger.log(` ${logger.dim(`id: \"${meta.id}\"`)}`)\n\tlogger.log(` ${logger.dim(`title: \"${meta.title}\"`)}`)\n\tlogger.log(` ${logger.dim(`route: \"${meta.route}\"`)}`)\n\tif (owner && owner.length > 0) {\n\t\tlogger.log(\n\t\t\t` ${logger.dim(`owner: [${owner.map((o) => `\"${o}\"`).join(\", \")}]`)}`,\n\t\t)\n\t}\n\tif (tags && tags.length > 0) {\n\t\tlogger.log(\n\t\t\t` ${logger.dim(`tags: [${tags.map((t) => `\"${t}\"`).join(\", \")}]`)}`,\n\t\t)\n\t}\n\tlogger.blank()\n}\n\n/**\n * Log summary after generation\n */\nfunction logSummary(created: number, skipped: number, dryRun: boolean): void {\n\tlogger.blank()\n\tif (dryRun) {\n\t\tlogger.info(`Would create ${created} files (${skipped} already exist)`)\n\t\tlogger.blank()\n\t\tlogger.log(`Run without ${logger.code(\"--dry-run\")} to create files`)\n\t} else {\n\t\tlogger.done(`Created ${created} files (${skipped} skipped)`)\n\t\tif (created > 0) {\n\t\t\tlogger.blank()\n\t\t\tlogger.log(logger.bold(\"Next steps:\"))\n\t\t\tlogger.log(\" 1. Review and customize the generated screen.meta.ts files\")\n\t\t\tlogger.log(\n\t\t\t\t` 2. Run ${logger.code(\"screenbook dev\")} to view your screen catalog`,\n\t\t\t)\n\t\t}\n\t}\n}\n\ninterface InferredScreenMeta {\n\tid: string\n\ttitle: string\n\troute: string\n}\n\ninterface InteractiveResult {\n\tskip: boolean\n\tmeta: InferredScreenMeta\n\towner: string[]\n\ttags: string[]\n}\n\n/**\n * Parse comma-separated string into array\n */\nexport function parseCommaSeparated(input: string): string[] {\n\tif (!input.trim()) return []\n\treturn input\n\t\t.split(\",\")\n\t\t.map((s) => s.trim())\n\t\t.filter(Boolean)\n}\n\n/**\n * Prompt user for screen metadata in interactive mode\n */\nasync function promptForScreen(\n\trouteFile: string,\n\tinferred: InferredScreenMeta,\n): Promise<InteractiveResult> {\n\tlogger.blank()\n\tlogger.info(`Found: ${logger.path(routeFile)}`)\n\tlogger.blank()\n\tlogger.log(\n\t\t` ${logger.dim(\"ID:\")} ${inferred.id} ${logger.dim(\"(inferred)\")}`,\n\t)\n\tlogger.log(\n\t\t` ${logger.dim(\"Title:\")} ${inferred.title} ${logger.dim(\"(inferred)\")}`,\n\t)\n\tlogger.log(\n\t\t` ${logger.dim(\"Route:\")} ${inferred.route} ${logger.dim(\"(inferred)\")}`,\n\t)\n\tlogger.blank()\n\n\tconst response = await prompts([\n\t\t{\n\t\t\ttype: \"confirm\",\n\t\t\tname: \"proceed\",\n\t\t\tmessage: \"Generate this screen?\",\n\t\t\tinitial: true,\n\t\t},\n\t\t{\n\t\t\ttype: (prev) => (prev ? \"text\" : null),\n\t\t\tname: \"id\",\n\t\t\tmessage: \"ID\",\n\t\t\tinitial: inferred.id,\n\t\t},\n\t\t{\n\t\t\ttype: (_prev, values) => (values.proceed ? \"text\" : null),\n\t\t\tname: \"title\",\n\t\t\tmessage: \"Title\",\n\t\t\tinitial: inferred.title,\n\t\t},\n\t\t{\n\t\t\ttype: (_prev, values) => (values.proceed ? \"text\" : null),\n\t\t\tname: \"owner\",\n\t\t\tmessage: \"Owner (comma-separated)\",\n\t\t\tinitial: \"\",\n\t\t},\n\t\t{\n\t\t\ttype: (_prev, values) => (values.proceed ? \"text\" : null),\n\t\t\tname: \"tags\",\n\t\t\tmessage: \"Tags (comma-separated)\",\n\t\t\tinitial: inferred.id.split(\".\")[0] || \"\",\n\t\t},\n\t])\n\n\tif (!response.proceed) {\n\t\treturn { skip: true, meta: inferred, owner: [], tags: [] }\n\t}\n\n\treturn {\n\t\tskip: false,\n\t\tmeta: {\n\t\t\tid: response.id || inferred.id,\n\t\t\ttitle: response.title || inferred.title,\n\t\t\troute: inferred.route,\n\t\t},\n\t\towner: parseCommaSeparated(response.owner || \"\"),\n\t\ttags: parseCommaSeparated(response.tags || \"\"),\n\t}\n}\n\n/**\n * Infer screen metadata from the route file path\n */\nfunction inferScreenMeta(\n\trouteDir: string,\n\troutesPattern: string,\n): InferredScreenMeta {\n\t// Extract base directory from pattern (e.g., \"src/pages\" from \"src/pages/**/page.tsx\")\n\tconst patternBase = routesPattern.split(\"*\")[0]?.replace(/\\/$/, \"\") ?? \"\"\n\n\t// Get relative path from pattern base\n\tconst relativePath = relative(patternBase, routeDir)\n\n\t// Handle root route\n\tif (!relativePath || relativePath === \".\") {\n\t\treturn {\n\t\t\tid: \"home\",\n\t\t\ttitle: \"Home\",\n\t\t\troute: \"/\",\n\t\t}\n\t}\n\n\t// Clean up path segments (remove route groups like (marketing), handle dynamic segments)\n\tconst segments = relativePath\n\t\t.split(\"/\")\n\t\t.filter((s) => s && !s.startsWith(\"(\") && !s.endsWith(\")\"))\n\t\t.map((s) =>\n\t\t\ts.replace(/^\\[\\.\\.\\..*\\]$/, \"catchall\").replace(/^\\[(.+)\\]$/, \"$1\"),\n\t\t)\n\n\t// Generate ID from segments (e.g., \"billing.invoice.detail\")\n\tconst id = segments.join(\".\")\n\n\t// Generate title from last segment (e.g., \"Invoice Detail\" from \"invoice-detail\")\n\tconst lastSegment = segments[segments.length - 1] || \"home\"\n\tconst title = lastSegment\n\t\t.split(/[-_]/)\n\t\t.map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n\t\t.join(\" \")\n\n\t// Generate route from path (e.g., \"/billing/invoice/:id\")\n\tconst routeSegments = relativePath\n\t\t.split(\"/\")\n\t\t.filter((s) => s && !s.startsWith(\"(\") && !s.endsWith(\")\"))\n\t\t.map((s) => {\n\t\t\t// Convert [id] to :id, [...slug] to *\n\t\t\tif (s.startsWith(\"[...\") && s.endsWith(\"]\")) {\n\t\t\t\treturn \"*\"\n\t\t\t}\n\t\t\tif (s.startsWith(\"[\") && s.endsWith(\"]\")) {\n\t\t\t\treturn `:${s.slice(1, -1)}`\n\t\t\t}\n\t\t\treturn s\n\t\t})\n\tconst route = `/${routeSegments.join(\"/\")}`\n\n\treturn { id, title, route }\n}\n\ninterface GenerateOptions {\n\towner?: string[]\n\ttags?: string[]\n}\n\n/**\n * Generate screen.meta.ts file content\n */\nfunction generateScreenMetaContent(\n\tmeta: InferredScreenMeta,\n\toptions?: GenerateOptions,\n): string {\n\t// Use provided values or infer defaults\n\tconst owner = options?.owner ?? []\n\tconst tags =\n\t\toptions?.tags && options.tags.length > 0\n\t\t\t? options.tags\n\t\t\t: [meta.id.split(\".\")[0] || \"general\"]\n\n\tconst ownerStr =\n\t\towner.length > 0 ? `[${owner.map((o) => `\"${o}\"`).join(\", \")}]` : \"[]\"\n\tconst tagsStr = `[${tags.map((t) => `\"${t}\"`).join(\", \")}]`\n\n\treturn `import { defineScreen } from \"@screenbook/core\"\n\nexport const screen = defineScreen({\n\tid: \"${meta.id}\",\n\ttitle: \"${meta.title}\",\n\troute: \"${meta.route}\",\n\n\t// Team or individual responsible for this screen\n\towner: ${ownerStr},\n\n\t// Tags for filtering in the catalog\n\ttags: ${tagsStr},\n\n\t// APIs/services this screen depends on (for impact analysis)\n\t// Example: [\"UserAPI.getProfile\", \"PaymentService.checkout\"]\n\tdependsOn: [],\n\n\t// Screen IDs that can navigate to this screen\n\tentryPoints: [],\n\n\t// Screen IDs this screen can navigate to\n\tnext: [],\n})\n`\n}\n","import type { Screen } from \"@screenbook/core\"\n\nexport interface TransitiveDependency {\n\tscreen: Screen\n\tpath: string[]\n}\n\nexport interface ImpactResult {\n\tapi: string\n\tdirect: Screen[]\n\ttransitive: TransitiveDependency[]\n\ttotalCount: number\n}\n\n/**\n * Check if a screen's dependsOn matches the API name (supports partial matching).\n * - \"InvoiceAPI\" matches \"InvoiceAPI.getDetail\"\n * - \"InvoiceAPI.getDetail\" matches \"InvoiceAPI.getDetail\"\n */\nfunction matchesDependency(dependency: string, apiName: string): boolean {\n\t// Exact match\n\tif (dependency === apiName) {\n\t\treturn true\n\t}\n\t// Partial match: apiName is a prefix of dependency\n\tif (dependency.startsWith(`${apiName}.`)) {\n\t\treturn true\n\t}\n\t// Partial match: dependency is a prefix of apiName\n\tif (apiName.startsWith(`${dependency}.`)) {\n\t\treturn true\n\t}\n\treturn false\n}\n\n/**\n * Find screens that directly depend on the given API.\n */\nfunction findDirectDependents(screens: Screen[], apiName: string): Screen[] {\n\treturn screens.filter((screen) =>\n\t\tscreen.dependsOn?.some((dep) => matchesDependency(dep, apiName)),\n\t)\n}\n\n/**\n * Build a reverse navigation graph: screenId -> screens that can navigate to it.\n * This is built from the `entryPoints` field.\n */\nfunction _buildReverseNavigationGraph(\n\tscreens: Screen[],\n): Map<string, Set<string>> {\n\tconst graph = new Map<string, Set<string>>()\n\n\tfor (const screen of screens) {\n\t\t// entryPoints lists screens that can navigate TO this screen\n\t\t// So we want to find screens that navigate FROM here\n\t\t// Actually, we need to reverse: if A.entryPoints includes B,\n\t\t// then B can navigate to A, so B is affected if A is affected\n\n\t\t// For transitive analysis:\n\t\t// If screen A depends on API, and screen B has entryPoints: [\"A\"],\n\t\t// then B can navigate to A, meaning users can reach A from B\n\t\t// So if A is impacted, we should show B as transitively impacted\n\n\t\tif (!screen.entryPoints) continue\n\n\t\tfor (const entryPoint of screen.entryPoints) {\n\t\t\tif (!graph.has(screen.id)) {\n\t\t\t\tgraph.set(screen.id, new Set())\n\t\t\t}\n\t\t\tgraph.get(screen.id)?.add(entryPoint)\n\t\t}\n\t}\n\n\treturn graph\n}\n\n/**\n * Build a navigation graph from `next` field: screenId -> screens it can navigate to.\n */\nfunction buildNavigationGraph(screens: Screen[]): Map<string, Set<string>> {\n\tconst graph = new Map<string, Set<string>>()\n\n\tfor (const screen of screens) {\n\t\tif (!screen.next) continue\n\n\t\tif (!graph.has(screen.id)) {\n\t\t\tgraph.set(screen.id, new Set())\n\t\t}\n\t\tfor (const nextId of screen.next) {\n\t\t\tgraph.get(screen.id)?.add(nextId)\n\t\t}\n\t}\n\n\treturn graph\n}\n\n/**\n * Find all transitive dependents using BFS.\n * A screen is transitively dependent if it can navigate to a directly dependent screen.\n */\nfunction findTransitiveDependents(\n\tscreens: Screen[],\n\tdirectDependentIds: Set<string>,\n\tmaxDepth: number,\n): TransitiveDependency[] {\n\tconst _screenMap = new Map(screens.map((s) => [s.id, s]))\n\tconst navigationGraph = buildNavigationGraph(screens)\n\tconst transitive: TransitiveDependency[] = []\n\tconst visited = new Set<string>()\n\n\t// For each screen, check if it can reach a directly dependent screen\n\tfor (const screen of screens) {\n\t\tif (directDependentIds.has(screen.id)) {\n\t\t\tcontinue // Skip direct dependents\n\t\t}\n\n\t\tconst path = findPathToDirectDependent(\n\t\t\tscreen.id,\n\t\t\tdirectDependentIds,\n\t\t\tnavigationGraph,\n\t\t\tmaxDepth,\n\t\t\tnew Set(),\n\t\t)\n\n\t\tif (path && !visited.has(screen.id)) {\n\t\t\tvisited.add(screen.id)\n\t\t\ttransitive.push({\n\t\t\t\tscreen,\n\t\t\t\tpath,\n\t\t\t})\n\t\t}\n\t}\n\n\treturn transitive\n}\n\n/**\n * Find a path from a screen to any directly dependent screen using BFS.\n */\nfunction findPathToDirectDependent(\n\tstartId: string,\n\ttargetIds: Set<string>,\n\tgraph: Map<string, Set<string>>,\n\tmaxDepth: number,\n\tvisited: Set<string>,\n): string[] | null {\n\tif (visited.has(startId)) {\n\t\treturn null\n\t}\n\n\tconst queue: Array<{ id: string; path: string[] }> = [\n\t\t{ id: startId, path: [startId] },\n\t]\n\tconst localVisited = new Set<string>([startId])\n\n\twhile (queue.length > 0) {\n\t\tconst current = queue.shift()\n\t\tif (!current) break\n\n\t\tif (current.path.length > maxDepth + 1) {\n\t\t\tcontinue\n\t\t}\n\n\t\tconst neighbors = graph.get(current.id)\n\t\tif (!neighbors) {\n\t\t\tcontinue\n\t\t}\n\n\t\tfor (const neighborId of neighbors) {\n\t\t\tif (localVisited.has(neighborId)) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tconst newPath = [...current.path, neighborId]\n\n\t\t\t// Check if path exceeds maxDepth (path includes start, so maxDepth+1 is the max length)\n\t\t\tif (newPath.length > maxDepth + 1) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif (targetIds.has(neighborId)) {\n\t\t\t\treturn newPath\n\t\t\t}\n\n\t\t\tlocalVisited.add(neighborId)\n\t\t\tqueue.push({ id: neighborId, path: newPath })\n\t\t}\n\t}\n\n\treturn null\n}\n\n/**\n * Analyze the impact of a change to an API on the screen catalog.\n */\nexport function analyzeImpact(\n\tscreens: Screen[],\n\tapiName: string,\n\tmaxDepth = 3,\n): ImpactResult {\n\t// Find direct dependents\n\tconst direct = findDirectDependents(screens, apiName)\n\tconst directIds = new Set(direct.map((s) => s.id))\n\n\t// Find transitive dependents\n\tconst transitive = findTransitiveDependents(screens, directIds, maxDepth)\n\n\treturn {\n\t\tapi: apiName,\n\t\tdirect,\n\t\ttransitive,\n\t\ttotalCount: direct.length + transitive.length,\n\t}\n}\n\n/**\n * Format the impact result as text output.\n */\nexport function formatImpactText(result: ImpactResult): string {\n\tconst lines: string[] = []\n\n\tlines.push(`Impact Analysis: ${result.api}`)\n\tlines.push(\"\")\n\n\tif (result.direct.length > 0) {\n\t\tlines.push(\n\t\t\t`Direct (${result.direct.length} screen${result.direct.length > 1 ? \"s\" : \"\"}):`,\n\t\t)\n\t\tfor (const screen of result.direct) {\n\t\t\tconst owner = screen.owner?.length ? ` [${screen.owner.join(\", \")}]` : \"\"\n\t\t\tlines.push(` - ${screen.id} ${screen.route}${owner}`)\n\t\t}\n\t\tlines.push(\"\")\n\t}\n\n\tif (result.transitive.length > 0) {\n\t\tlines.push(\n\t\t\t`Transitive (${result.transitive.length} screen${result.transitive.length > 1 ? \"s\" : \"\"}):`,\n\t\t)\n\t\tfor (const { path } of result.transitive) {\n\t\t\tlines.push(` - ${path.join(\" -> \")}`)\n\t\t}\n\t\tlines.push(\"\")\n\t}\n\n\tif (result.totalCount === 0) {\n\t\tlines.push(\"No screens depend on this API.\")\n\t\tlines.push(\"\")\n\t} else {\n\t\tlines.push(\n\t\t\t`Total: ${result.totalCount} screen${result.totalCount > 1 ? \"s\" : \"\"} affected`,\n\t\t)\n\t}\n\n\treturn lines.join(\"\\n\")\n}\n\n/**\n * Format the impact result as JSON output.\n */\nexport function formatImpactJson(result: ImpactResult): string {\n\treturn JSON.stringify(\n\t\t{\n\t\t\tapi: result.api,\n\t\t\tsummary: {\n\t\t\t\tdirectCount: result.direct.length,\n\t\t\t\ttransitiveCount: result.transitive.length,\n\t\t\t\ttotalCount: result.totalCount,\n\t\t\t},\n\t\t\tdirect: result.direct.map((s) => ({\n\t\t\t\tid: s.id,\n\t\t\t\ttitle: s.title,\n\t\t\t\troute: s.route,\n\t\t\t\towner: s.owner,\n\t\t\t})),\n\t\t\ttransitive: result.transitive.map(({ screen, path }) => ({\n\t\t\t\tid: screen.id,\n\t\t\t\ttitle: screen.title,\n\t\t\t\troute: screen.route,\n\t\t\t\tpath,\n\t\t\t})),\n\t\t},\n\t\tnull,\n\t\t2,\n\t)\n}\n","import { existsSync, readFileSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport type { Screen } from \"@screenbook/core\"\nimport { define } from \"gunshi\"\nimport { loadConfig } from \"../utils/config.js\"\nimport { ERRORS } from \"../utils/errors.js\"\nimport {\n\tanalyzeImpact,\n\tformatImpactJson,\n\tformatImpactText,\n} from \"../utils/impactAnalysis.js\"\nimport { logger } from \"../utils/logger.js\"\n\nexport const impactCommand = define({\n\tname: \"impact\",\n\tdescription: \"Analyze which screens depend on a specific API/service\",\n\targs: {\n\t\tapi: {\n\t\t\ttype: \"positional\",\n\t\t\tdescription:\n\t\t\t\t\"API or service name to analyze (e.g., InvoiceAPI.getDetail)\",\n\t\t\trequired: true,\n\t\t},\n\t\tconfig: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"c\",\n\t\t\tdescription: \"Path to config file\",\n\t\t},\n\t\tformat: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"f\",\n\t\t\tdescription: \"Output format: text (default) or json\",\n\t\t\tdefault: \"text\",\n\t\t},\n\t\tdepth: {\n\t\t\ttype: \"number\",\n\t\t\tshort: \"d\",\n\t\t\tdescription: \"Maximum depth for transitive dependencies\",\n\t\t\tdefault: 3,\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tconst config = await loadConfig(ctx.values.config)\n\t\tconst cwd = process.cwd()\n\n\t\tconst apiName = ctx.values.api\n\t\tif (!apiName) {\n\t\t\tlogger.errorWithHelp(ERRORS.API_NAME_REQUIRED)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tconst format = ctx.values.format ?? \"text\"\n\t\tconst depth = ctx.values.depth ?? 3\n\n\t\t// Load screens.json\n\t\tconst screensPath = join(cwd, config.outDir, \"screens.json\")\n\n\t\tif (!existsSync(screensPath)) {\n\t\t\tlogger.errorWithHelp(ERRORS.SCREENS_NOT_FOUND)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tlet screens: Screen[]\n\t\ttry {\n\t\t\tconst content = readFileSync(screensPath, \"utf-8\")\n\t\t\tscreens = JSON.parse(content) as Screen[]\n\t\t} catch (error) {\n\t\t\tlogger.errorWithHelp({\n\t\t\t\t...ERRORS.SCREENS_PARSE_ERROR,\n\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t})\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tif (screens.length === 0) {\n\t\t\tlogger.warn(\"No screens found in the catalog.\")\n\t\t\tlogger.blank()\n\t\t\tlogger.log(\"Run 'screenbook generate' to create screen.meta.ts files,\")\n\t\t\tlogger.log(\"then 'screenbook build' to generate the catalog.\")\n\t\t\treturn\n\t\t}\n\n\t\t// Analyze impact\n\t\tconst result = analyzeImpact(screens, apiName, depth)\n\n\t\t// Output result\n\t\tif (format === \"json\") {\n\t\t\tlogger.log(formatImpactJson(result))\n\t\t} else {\n\t\t\tlogger.log(formatImpactText(result))\n\t\t}\n\t},\n})\n","import { existsSync, readFileSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport prompts from \"prompts\"\n\nexport interface FrameworkInfo {\n\tname: string\n\troutesPattern: string\n\tmetaPattern: string\n}\n\ninterface FrameworkDefinition extends FrameworkInfo {\n\tpackages: string[]\n\tconfigFiles: string[]\n\t/**\n\t * Additional check to distinguish variants (e.g., App Router vs Pages Router)\n\t */\n\tcheck?: (cwd: string) => boolean\n}\n\nconst FRAMEWORKS: FrameworkDefinition[] = [\n\t{\n\t\tname: \"Next.js (App Router)\",\n\t\tpackages: [\"next\"],\n\t\tconfigFiles: [\"next.config.js\", \"next.config.mjs\", \"next.config.ts\"],\n\t\troutesPattern: \"app/**/page.tsx\",\n\t\tmetaPattern: \"app/**/screen.meta.ts\",\n\t\tcheck: (cwd) =>\n\t\t\texistsSync(join(cwd, \"app\")) || existsSync(join(cwd, \"src/app\")),\n\t},\n\t{\n\t\tname: \"Next.js (Pages Router)\",\n\t\tpackages: [\"next\"],\n\t\tconfigFiles: [\"next.config.js\", \"next.config.mjs\", \"next.config.ts\"],\n\t\troutesPattern: \"pages/**/*.tsx\",\n\t\tmetaPattern: \"pages/**/screen.meta.ts\",\n\t\tcheck: (cwd) =>\n\t\t\texistsSync(join(cwd, \"pages\")) || existsSync(join(cwd, \"src/pages\")),\n\t},\n\t{\n\t\tname: \"Remix\",\n\t\tpackages: [\"@remix-run/react\", \"remix\"],\n\t\tconfigFiles: [\"remix.config.js\", \"vite.config.ts\"],\n\t\troutesPattern: \"app/routes/**/*.tsx\",\n\t\tmetaPattern: \"app/routes/**/screen.meta.ts\",\n\t},\n\t{\n\t\tname: \"Nuxt\",\n\t\tpackages: [\"nuxt\"],\n\t\tconfigFiles: [\"nuxt.config.ts\", \"nuxt.config.js\", \"nuxt.config.mjs\"],\n\t\troutesPattern: \"pages/**/*.vue\",\n\t\tmetaPattern: \"pages/**/screen.meta.ts\",\n\t\tcheck: (cwd) => {\n\t\t\t// Nuxt 4 uses app/pages, Nuxt 3 uses pages\n\t\t\tif (existsSync(join(cwd, \"app/pages\"))) {\n\t\t\t\treturn false // Will be handled by Nuxt 4 definition\n\t\t\t}\n\t\t\treturn existsSync(join(cwd, \"pages\"))\n\t\t},\n\t},\n\t{\n\t\tname: \"Nuxt 4\",\n\t\tpackages: [\"nuxt\"],\n\t\tconfigFiles: [\"nuxt.config.ts\", \"nuxt.config.js\"],\n\t\troutesPattern: \"app/pages/**/*.vue\",\n\t\tmetaPattern: \"app/pages/**/screen.meta.ts\",\n\t\tcheck: (cwd) => existsSync(join(cwd, \"app/pages\")),\n\t},\n\t{\n\t\tname: \"Astro\",\n\t\tpackages: [\"astro\"],\n\t\tconfigFiles: [\n\t\t\t\"astro.config.mjs\",\n\t\t\t\"astro.config.js\",\n\t\t\t\"astro.config.ts\",\n\t\t\t\"astro.config.cjs\",\n\t\t],\n\t\troutesPattern: \"src/pages/**/*.astro\",\n\t\tmetaPattern: \"src/pages/**/screen.meta.ts\",\n\t},\n\t{\n\t\tname: \"SolidStart\",\n\t\tpackages: [\"@solidjs/start\"],\n\t\tconfigFiles: [\"app.config.ts\", \"app.config.js\"],\n\t\troutesPattern: \"src/routes/**/*.tsx\",\n\t\tmetaPattern: \"src/routes/**/screen.meta.ts\",\n\t\tcheck: (cwd) => existsSync(join(cwd, \"src/routes\")),\n\t},\n\t{\n\t\tname: \"QwikCity\",\n\t\tpackages: [\"@builder.io/qwik-city\"],\n\t\tconfigFiles: [\"vite.config.ts\", \"vite.config.js\", \"vite.config.mjs\"],\n\t\t// QwikCity uses index.tsx files as page components (e.g., about/index.tsx not about.tsx)\n\t\troutesPattern: \"src/routes/**/index.tsx\",\n\t\tmetaPattern: \"src/routes/**/screen.meta.ts\",\n\t\tcheck: (cwd) => existsSync(join(cwd, \"src/routes\")),\n\t},\n\t{\n\t\tname: \"TanStack Start\",\n\t\tpackages: [\"@tanstack/react-start\", \"@tanstack/start\"],\n\t\tconfigFiles: [\"app.config.ts\", \"app.config.js\"],\n\t\troutesPattern: \"src/routes/**/*.tsx\",\n\t\tmetaPattern: \"src/routes/**/screen.meta.ts\",\n\t\t// TanStack Start requires __root.tsx for file-based routing\n\t\tcheck: (cwd) => existsSync(join(cwd, \"src/routes/__root.tsx\")),\n\t},\n\t{\n\t\tname: \"Vite + Vue\",\n\t\tpackages: [\"vite\", \"vue\"],\n\t\tconfigFiles: [\"vite.config.ts\", \"vite.config.js\", \"vite.config.mjs\"],\n\t\troutesPattern: \"src/pages/**/*.vue\",\n\t\tmetaPattern: \"src/pages/**/screen.meta.ts\",\n\t\t// Check that react is NOT present to avoid matching React projects\n\t\tcheck: (cwd) => {\n\t\t\tconst packageJson = readPackageJson(cwd)\n\t\t\tif (!packageJson) return false\n\t\t\treturn !hasPackage(packageJson, \"react\")\n\t\t},\n\t},\n\t{\n\t\tname: \"Vite + React\",\n\t\tpackages: [\"vite\", \"react\"],\n\t\tconfigFiles: [\"vite.config.ts\", \"vite.config.js\", \"vite.config.mjs\"],\n\t\troutesPattern: \"src/pages/**/*.tsx\",\n\t\tmetaPattern: \"src/pages/**/screen.meta.ts\",\n\t},\n]\n\ninterface PackageJson {\n\tdependencies?: Record<string, string>\n\tdevDependencies?: Record<string, string>\n}\n\nfunction readPackageJson(cwd: string): PackageJson | null {\n\tconst packageJsonPath = join(cwd, \"package.json\")\n\tif (!existsSync(packageJsonPath)) {\n\t\treturn null\n\t}\n\ttry {\n\t\tconst content = readFileSync(packageJsonPath, \"utf-8\")\n\t\treturn JSON.parse(content)\n\t} catch (error) {\n\t\tconsole.warn(\n\t\t\t`Warning: Failed to parse package.json at ${packageJsonPath}: ${error instanceof Error ? error.message : String(error)}`,\n\t\t)\n\t\treturn null\n\t}\n}\n\nfunction hasPackage(packageJson: PackageJson, packageName: string): boolean {\n\treturn !!(\n\t\tpackageJson.dependencies?.[packageName] ||\n\t\tpackageJson.devDependencies?.[packageName]\n\t)\n}\n\nfunction hasConfigFile(cwd: string, configFiles: string[]): boolean {\n\treturn configFiles.some((file) => existsSync(join(cwd, file)))\n}\n\n/**\n * Auto-detect the frontend framework in a project directory.\n * Returns framework info if detected, null otherwise.\n */\nexport function detectFramework(cwd: string): FrameworkInfo | null {\n\tconst packageJson = readPackageJson(cwd)\n\tif (!packageJson) {\n\t\treturn null\n\t}\n\n\tfor (const framework of FRAMEWORKS) {\n\t\t// Check if required packages are present\n\t\tconst hasRequiredPackage = framework.packages.some((pkg) =>\n\t\t\thasPackage(packageJson, pkg),\n\t\t)\n\t\tif (!hasRequiredPackage) {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Check for config files\n\t\tconst hasConfig = hasConfigFile(cwd, framework.configFiles)\n\t\tif (!hasConfig) {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Run additional check if defined\n\t\tif (framework.check && !framework.check(cwd)) {\n\t\t\tcontinue\n\t\t}\n\n\t\treturn {\n\t\t\tname: framework.name,\n\t\t\troutesPattern: framework.routesPattern,\n\t\t\tmetaPattern: framework.metaPattern,\n\t\t}\n\t}\n\n\treturn null\n}\n\n/**\n * Interactive framework selection when auto-detection fails.\n */\nexport async function promptFrameworkSelection(): Promise<FrameworkInfo | null> {\n\tconst choices = FRAMEWORKS.filter(\n\t\t// Remove duplicates (e.g., Nuxt 4 vs Nuxt)\n\t\t(fw, idx, arr) =>\n\t\t\tarr.findIndex((f) => f.routesPattern === fw.routesPattern) === idx,\n\t).map((fw) => ({\n\t\ttitle: fw.name,\n\t\tvalue: fw,\n\t}))\n\n\tchoices.push({\n\t\ttitle: \"Other (manual configuration)\",\n\t\tvalue: null as unknown as FrameworkDefinition,\n\t})\n\n\tconst response = await prompts({\n\t\ttype: \"select\",\n\t\tname: \"framework\",\n\t\tmessage: \"Select your frontend framework:\",\n\t\tchoices,\n\t})\n\n\tif (!response.framework) {\n\t\treturn null\n\t}\n\n\treturn {\n\t\tname: response.framework.name,\n\t\troutesPattern: response.framework.routesPattern,\n\t\tmetaPattern: response.framework.metaPattern,\n\t}\n}\n\n/**\n * Detect framework or prompt user if detection fails.\n */\nexport async function detectOrPromptFramework(\n\tcwd: string,\n): Promise<FrameworkInfo | null> {\n\tconst detected = detectFramework(cwd)\n\tif (detected) {\n\t\treturn detected\n\t}\n\treturn promptFrameworkSelection()\n}\n","import { existsSync, readFileSync, writeFileSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport { define } from \"gunshi\"\nimport {\n\tdetectFramework,\n\ttype FrameworkInfo,\n\tpromptFrameworkSelection,\n} from \"../utils/detectFramework.js\"\nimport { logger } from \"../utils/logger.js\"\n\nfunction generateConfigTemplate(framework: FrameworkInfo | null): string {\n\tif (framework) {\n\t\treturn `import { defineConfig } from \"@screenbook/core\"\n\nexport default defineConfig({\n\t// Auto-detected: ${framework.name}\n\tmetaPattern: \"${framework.metaPattern}\",\n\troutesPattern: \"${framework.routesPattern}\",\n\toutDir: \".screenbook\",\n})\n`\n\t}\n\n\t// Fallback template when no framework detected\n\treturn `import { defineConfig } from \"@screenbook/core\"\n\nexport default defineConfig({\n\t// Glob pattern for screen metadata files\n\tmetaPattern: \"src/**/screen.meta.ts\",\n\n\t// Glob pattern for route files (uncomment and adjust for your framework):\n\t// routesPattern: \"src/pages/**/page.tsx\", // Vite/React\n\t// routesPattern: \"app/**/page.tsx\", // Next.js App Router\n\t// routesPattern: \"pages/**/*.tsx\", // Next.js Pages Router\n\t// routesPattern: \"app/routes/**/*.tsx\", // Remix\n\t// routesPattern: \"pages/**/*.vue\", // Nuxt\n\t// routesPattern: \"src/pages/**/*.astro\", // Astro\n\n\toutDir: \".screenbook\",\n})\n`\n}\n\nfunction printValueProposition(): void {\n\tlogger.blank()\n\tlogger.log(logger.bold(\"What Screenbook gives you:\"))\n\tlogger.log(\" - Screen catalog with search & filter\")\n\tlogger.log(\" - Navigation graph visualization\")\n\tlogger.log(\" - Impact analysis (API -> affected screens)\")\n\tlogger.log(\" - CI lint for documentation coverage\")\n}\n\nfunction printNextSteps(hasRoutesPattern: boolean): void {\n\tlogger.blank()\n\tlogger.log(logger.bold(\"Next steps:\"))\n\tif (hasRoutesPattern) {\n\t\tlogger.log(\n\t\t\t` 1. Run ${logger.code(\"screenbook generate\")} to auto-create screen.meta.ts files`,\n\t\t)\n\t\tlogger.log(\n\t\t\t` 2. Run ${logger.code(\"screenbook dev\")} to start the UI server`,\n\t\t)\n\t} else {\n\t\tlogger.log(\" 1. Configure routesPattern in screenbook.config.ts\")\n\t\tlogger.log(\n\t\t\t` 2. Run ${logger.code(\"screenbook generate\")} to auto-create screen.meta.ts files`,\n\t\t)\n\t\tlogger.log(\n\t\t\t` 3. Run ${logger.code(\"screenbook dev\")} to start the UI server`,\n\t\t)\n\t}\n\tlogger.blank()\n\tlogger.log(\"screen.meta.ts files are created alongside your route files:\")\n\tlogger.blank()\n\tlogger.log(logger.dim(\" src/pages/dashboard/\"))\n\tlogger.log(logger.dim(\" page.tsx # Your route file\"))\n\tlogger.log(\n\t\tlogger.dim(\" screen.meta.ts # Auto-generated, customize as needed\"),\n\t)\n}\n\nexport const initCommand = define({\n\tname: \"init\",\n\tdescription: \"Initialize Screenbook in a project\",\n\targs: {\n\t\tforce: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"f\",\n\t\t\tdescription: \"Overwrite existing files\",\n\t\t\tdefault: false,\n\t\t},\n\t\tskipDetect: {\n\t\t\ttype: \"boolean\",\n\t\t\tdescription: \"Skip framework auto-detection\",\n\t\t\tdefault: false,\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tconst cwd = process.cwd()\n\t\tconst force = ctx.values.force ?? false\n\t\tconst skipDetect = ctx.values.skipDetect ?? false\n\n\t\tlogger.info(\"Initializing Screenbook...\")\n\t\tlogger.blank()\n\n\t\t// Framework detection\n\t\tlet framework: FrameworkInfo | null = null\n\n\t\tif (!skipDetect) {\n\t\t\tframework = detectFramework(cwd)\n\n\t\t\tif (framework) {\n\t\t\t\tlogger.itemSuccess(`Detected: ${framework.name}`)\n\t\t\t} else {\n\t\t\t\tlogger.log(\" Could not auto-detect framework\")\n\t\t\t\tlogger.blank()\n\t\t\t\tframework = await promptFrameworkSelection()\n\n\t\t\t\tif (framework) {\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tlogger.itemSuccess(`Selected: ${framework.name}`)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Create screenbook.config.ts\n\t\tconst configPath = join(cwd, \"screenbook.config.ts\")\n\t\tif (!force && existsSync(configPath)) {\n\t\t\tlogger.log(\n\t\t\t\t` ${logger.dim(\"-\")} screenbook.config.ts already exists ${logger.dim(\"(skipped)\")}`,\n\t\t\t)\n\t\t} else {\n\t\t\tconst configContent = generateConfigTemplate(framework)\n\t\t\twriteFileSync(configPath, configContent)\n\t\t\tlogger.itemSuccess(\"Created screenbook.config.ts\")\n\t\t}\n\n\t\t// Update .gitignore\n\t\tconst gitignorePath = join(cwd, \".gitignore\")\n\t\tconst screenbookIgnore = \".screenbook\"\n\n\t\tif (existsSync(gitignorePath)) {\n\t\t\tconst gitignoreContent = readFileSync(gitignorePath, \"utf-8\")\n\t\t\tif (!gitignoreContent.includes(screenbookIgnore)) {\n\t\t\t\tconst newContent = `${gitignoreContent.trimEnd()}\\n\\n# Screenbook\\n${screenbookIgnore}\\n`\n\t\t\t\twriteFileSync(gitignorePath, newContent)\n\t\t\t\tlogger.itemSuccess(\"Added .screenbook to .gitignore\")\n\t\t\t} else {\n\t\t\t\tlogger.log(\n\t\t\t\t\t` ${logger.dim(\"-\")} .screenbook already in .gitignore ${logger.dim(\"(skipped)\")}`,\n\t\t\t\t)\n\t\t\t}\n\t\t} else {\n\t\t\twriteFileSync(gitignorePath, `# Screenbook\\n${screenbookIgnore}\\n`)\n\t\t\tlogger.itemSuccess(\"Created .gitignore with .screenbook\")\n\t\t}\n\n\t\tlogger.blank()\n\t\tlogger.done(\"Screenbook initialized successfully!\")\n\n\t\tprintValueProposition()\n\t\tprintNextSteps(framework !== null)\n\t},\n})\n","import { existsSync, readFileSync } from \"node:fs\"\nimport { dirname, join, relative, resolve } from \"node:path\"\nimport type { AdoptionConfig, Config, Screen } from \"@screenbook/core\"\nimport { define } from \"gunshi\"\nimport { minimatch } from \"minimatch\"\nimport { glob } from \"tinyglobby\"\nimport { parseAngularRouterConfig } from \"../utils/angularRouterParser.js\"\nimport { loadConfig } from \"../utils/config.js\"\nimport {\n\tdetectCycles,\n\tformatCycleWarnings,\n\tgetCycleSummary,\n} from \"../utils/cycleDetection.js\"\nimport { ERRORS } from \"../utils/errors.js\"\nimport { logger } from \"../utils/logger.js\"\nimport {\n\tdetectRouterType,\n\tparseReactRouterConfig,\n} from \"../utils/reactRouterParser.js\"\nimport type { FlatRoute, ParseResult } from \"../utils/routeParserUtils.js\"\nimport { flattenRoutes } from \"../utils/routeParserUtils.js\"\nimport { parseSolidRouterConfig } from \"../utils/solidRouterParser.js\"\nimport { parseTanStackRouterConfig } from \"../utils/tanstackRouterParser.js\"\nimport { parseVueRouterConfig } from \"../utils/vueRouterParser.js\"\n\nexport const lintCommand = define({\n\tname: \"lint\",\n\tdescription: \"Detect routes without screen.meta.ts files\",\n\targs: {\n\t\tconfig: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"c\",\n\t\t\tdescription: \"Path to config file\",\n\t\t},\n\t\tallowCycles: {\n\t\t\ttype: \"boolean\",\n\t\t\tdescription: \"Suppress circular navigation warnings\",\n\t\t\tdefault: false,\n\t\t},\n\t\tstrict: {\n\t\t\ttype: \"boolean\",\n\t\t\tshort: \"s\",\n\t\t\tdescription: \"Fail on disallowed cycles\",\n\t\t\tdefault: false,\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tconst config = await loadConfig(ctx.values.config)\n\t\tconst cwd = process.cwd()\n\t\tconst adoption = config.adoption ?? { mode: \"full\" }\n\t\tlet hasWarnings = false\n\n\t\t// Check for routes configuration\n\t\tif (!config.routesPattern && !config.routesFile) {\n\t\t\tlogger.errorWithHelp(ERRORS.ROUTES_PATTERN_MISSING)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tlogger.info(\"Linting screen metadata coverage...\")\n\t\tif (adoption.mode === \"progressive\") {\n\t\t\tlogger.log(`Mode: Progressive adoption`)\n\t\t\tif (adoption.includePatterns?.length) {\n\t\t\t\tlogger.log(`Checking: ${adoption.includePatterns.join(\", \")}`)\n\t\t\t}\n\t\t\tif (adoption.minimumCoverage != null) {\n\t\t\t\tlogger.log(`Minimum coverage: ${adoption.minimumCoverage}%`)\n\t\t\t}\n\t\t}\n\t\tlogger.blank()\n\n\t\t// Use routesFile mode (config-based routing)\n\t\tif (config.routesFile) {\n\t\t\tawait lintRoutesFile(\n\t\t\t\tconfig.routesFile,\n\t\t\t\tcwd,\n\t\t\t\tconfig,\n\t\t\t\tadoption,\n\t\t\t\tctx.values.allowCycles ?? false,\n\t\t\t\tctx.values.strict ?? false,\n\t\t\t)\n\t\t\treturn\n\t\t}\n\n\t\t// Use routesPattern mode (file-based routing)\n\t\t// Find all route files\n\t\tlet routeFiles = await glob(config.routesPattern as string, {\n\t\t\tcwd,\n\t\t\tignore: config.ignore,\n\t\t})\n\n\t\t// In progressive mode, filter to only included patterns\n\t\tif (adoption.mode === \"progressive\" && adoption.includePatterns?.length) {\n\t\t\trouteFiles = routeFiles.filter((file) =>\n\t\t\t\tadoption.includePatterns?.some((pattern) => minimatch(file, pattern)),\n\t\t\t)\n\t\t}\n\n\t\tif (routeFiles.length === 0) {\n\t\t\tlogger.warn(`No route files found matching: ${config.routesPattern}`)\n\t\t\tif (adoption.mode === \"progressive\" && adoption.includePatterns?.length) {\n\t\t\t\tlogger.log(\n\t\t\t\t\t` ${logger.dim(`(filtered by includePatterns: ${adoption.includePatterns.join(\", \")})`)}`,\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\t// Find all screen.meta.ts files\n\t\tconst metaFiles = await glob(config.metaPattern, {\n\t\t\tcwd,\n\t\t\tignore: config.ignore,\n\t\t})\n\n\t\t// Build a set of directories that have screen.meta.ts\n\t\tconst metaDirs = new Set<string>()\n\t\tfor (const metaFile of metaFiles) {\n\t\t\tmetaDirs.add(dirname(metaFile))\n\t\t}\n\n\t\t// Check each route file - simple colocation check\n\t\tconst missingMeta: string[] = []\n\t\tconst covered: string[] = []\n\n\t\tfor (const routeFile of routeFiles) {\n\t\t\tconst routeDir = dirname(routeFile)\n\n\t\t\t// Check if there's a screen.meta.ts in the same directory\n\t\t\tif (metaDirs.has(routeDir)) {\n\t\t\t\tcovered.push(routeFile)\n\t\t\t} else {\n\t\t\t\tmissingMeta.push(routeFile)\n\t\t\t}\n\t\t}\n\n\t\t// Report results\n\t\tconst total = routeFiles.length\n\t\tconst coveredCount = covered.length\n\t\tconst missingCount = missingMeta.length\n\t\tconst coveragePercent = Math.round((coveredCount / total) * 100)\n\n\t\tlogger.log(`Found ${total} route files`)\n\t\tlogger.log(`Coverage: ${coveredCount}/${total} (${coveragePercent}%)`)\n\t\tlogger.blank()\n\n\t\t// Determine if lint should fail\n\t\tconst minimumCoverage = adoption.minimumCoverage ?? 100\n\t\tconst passedCoverage = coveragePercent >= minimumCoverage\n\n\t\tif (missingCount > 0) {\n\t\t\tlogger.log(`Missing screen.meta.ts (${missingCount} files):`)\n\t\t\tlogger.blank()\n\n\t\t\tfor (const file of missingMeta) {\n\t\t\t\tconst suggestedMetaPath = join(dirname(file), \"screen.meta.ts\")\n\t\t\t\tlogger.itemError(file)\n\t\t\t\tlogger.log(` ${logger.dim(\"→\")} ${logger.path(suggestedMetaPath)}`)\n\t\t\t}\n\n\t\t\tlogger.blank()\n\t\t}\n\n\t\tif (!passedCoverage) {\n\t\t\tlogger.error(\n\t\t\t\t`Lint failed: Coverage ${coveragePercent}% is below minimum ${minimumCoverage}%`,\n\t\t\t)\n\t\t\tprocess.exit(1)\n\t\t} else if (missingCount > 0) {\n\t\t\tlogger.success(\n\t\t\t\t`Coverage ${coveragePercent}% meets minimum ${minimumCoverage}%`,\n\t\t\t)\n\t\t\tif (adoption.mode === \"progressive\") {\n\t\t\t\tlogger.log(\n\t\t\t\t\t` ${logger.dim(\"Tip:\")} Increase minimumCoverage in config to gradually improve coverage`,\n\t\t\t\t)\n\t\t\t}\n\t\t} else {\n\t\t\tlogger.done(\"All routes have screen.meta.ts files\")\n\t\t}\n\n\t\t// Check for orphan screens (unreachable screens) and cycles\n\t\tconst screensPath = join(cwd, config.outDir, \"screens.json\")\n\t\tif (existsSync(screensPath)) {\n\t\t\ttry {\n\t\t\t\tconst content = readFileSync(screensPath, \"utf-8\")\n\t\t\t\tconst screens = JSON.parse(content) as Screen[]\n\n\t\t\t\t// Check for orphan screens\n\t\t\t\tconst orphans = findOrphanScreens(screens)\n\n\t\t\t\tif (orphans.length > 0) {\n\t\t\t\t\thasWarnings = true\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tlogger.warn(`Orphan screens detected (${orphans.length}):`)\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tlogger.log(\" These screens have no entryPoints and are not\")\n\t\t\t\t\tlogger.log(\" referenced in any other screen's 'next' array.\")\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tfor (const orphan of orphans) {\n\t\t\t\t\t\tlogger.itemWarn(`${orphan.id} ${logger.dim(orphan.route)}`)\n\t\t\t\t\t}\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t` ${logger.dim(\"Consider adding entryPoints or removing these screens.\")}`,\n\t\t\t\t\t)\n\t\t\t\t}\n\n\t\t\t\t// Check for circular navigation\n\t\t\t\tif (!ctx.values.allowCycles) {\n\t\t\t\t\tconst cycleResult = detectCycles(screens)\n\t\t\t\t\tif (cycleResult.hasCycles) {\n\t\t\t\t\t\thasWarnings = true\n\t\t\t\t\t\tlogger.blank()\n\t\t\t\t\t\tlogger.warn(getCycleSummary(cycleResult))\n\t\t\t\t\t\tlogger.log(formatCycleWarnings(cycleResult.cycles))\n\t\t\t\t\t\tlogger.blank()\n\t\t\t\t\t\tif (cycleResult.disallowedCycles.length > 0) {\n\t\t\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t\t\t` ${logger.dim(\"Use 'allowCycles: true' in screen.meta.ts to allow intentional cycles.\")}`,\n\t\t\t\t\t\t\t)\n\n\t\t\t\t\t\t\tif (ctx.values.strict) {\n\t\t\t\t\t\t\t\tlogger.blank()\n\t\t\t\t\t\t\t\tlogger.errorWithHelp(\n\t\t\t\t\t\t\t\t\tERRORS.CYCLES_DETECTED(cycleResult.disallowedCycles.length),\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\tprocess.exit(1)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Check for invalid navigation references\n\t\t\t\tconst invalidNavs = findInvalidNavigations(screens)\n\t\t\t\tif (invalidNavs.length > 0) {\n\t\t\t\t\thasWarnings = true\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tlogger.warn(`Invalid navigation targets (${invalidNavs.length}):`)\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t\" These navigation references point to non-existent screens.\",\n\t\t\t\t\t)\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tfor (const inv of invalidNavs) {\n\t\t\t\t\t\tlogger.itemWarn(\n\t\t\t\t\t\t\t`${inv.screenId} → ${logger.dim(inv.field)}: \"${inv.target}\"`,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t` ${logger.dim(\"Check that these screen IDs exist in your codebase.\")}`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\t// Handle specific error types\n\t\t\t\tif (error instanceof SyntaxError) {\n\t\t\t\t\tlogger.warn(\"Failed to parse screens.json - file may be corrupted\")\n\t\t\t\t\tlogger.log(` ${logger.dim(\"Run 'screenbook build' to regenerate.\")}`)\n\t\t\t\t\thasWarnings = true\n\t\t\t\t} else if (error instanceof Error) {\n\t\t\t\t\tlogger.warn(`Failed to analyze screens.json: ${error.message}`)\n\t\t\t\t\thasWarnings = true\n\t\t\t\t} else {\n\t\t\t\t\tlogger.warn(`Failed to analyze screens.json: ${String(error)}`)\n\t\t\t\t\thasWarnings = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (hasWarnings) {\n\t\t\tlogger.blank()\n\t\t\tlogger.warn(\"Lint completed with warnings.\")\n\t\t}\n\t},\n})\n\n/**\n * Lint screen.meta.ts coverage for routesFile mode (config-based routing)\n */\nasync function lintRoutesFile(\n\troutesFile: string,\n\tcwd: string,\n\tconfig: Pick<Config, \"metaPattern\" | \"outDir\" | \"ignore\">,\n\tadoption: AdoptionConfig,\n\tallowCycles: boolean,\n\tstrict: boolean,\n): Promise<boolean> {\n\tlet hasWarnings = false\n\tconst absoluteRoutesFile = resolve(cwd, routesFile)\n\n\t// Check if routes file exists\n\tif (!existsSync(absoluteRoutesFile)) {\n\t\tlogger.errorWithHelp(ERRORS.ROUTES_FILE_NOT_FOUND(routesFile))\n\t\tprocess.exit(1)\n\t}\n\n\tlogger.log(`Parsing routes from ${logger.path(routesFile)}...`)\n\tlogger.blank()\n\n\t// Parse the routes file with auto-detection\n\tlet flatRoutes: FlatRoute[]\n\ttry {\n\t\tconst content = readFileSync(absoluteRoutesFile, \"utf-8\")\n\t\tconst routerType = detectRouterType(content)\n\n\t\tlet parseResult: ParseResult\n\t\tif (routerType === \"tanstack-router\") {\n\t\t\tparseResult = parseTanStackRouterConfig(absoluteRoutesFile, content)\n\t\t} else if (routerType === \"solid-router\") {\n\t\t\tparseResult = parseSolidRouterConfig(absoluteRoutesFile, content)\n\t\t} else if (routerType === \"angular-router\") {\n\t\t\tparseResult = parseAngularRouterConfig(absoluteRoutesFile, content)\n\t\t} else if (routerType === \"react-router\") {\n\t\t\tparseResult = parseReactRouterConfig(absoluteRoutesFile, content)\n\t\t} else if (routerType === \"vue-router\") {\n\t\t\tparseResult = parseVueRouterConfig(absoluteRoutesFile, content)\n\t\t} else {\n\t\t\t// Unknown router type - warn user and attempt Vue Router parser as fallback\n\t\t\tlogger.warn(\n\t\t\t\t`Could not auto-detect router type for ${logger.path(routesFile)}. Attempting to parse as Vue Router.`,\n\t\t\t)\n\t\t\tlogger.log(\n\t\t\t\t` ${logger.dim(\"If parsing fails, check that your router imports are explicit.\")}`,\n\t\t\t)\n\t\t\thasWarnings = true\n\t\t\tparseResult = parseVueRouterConfig(absoluteRoutesFile, content)\n\t\t}\n\n\t\t// Show warnings\n\t\tfor (const warning of parseResult.warnings) {\n\t\t\tlogger.warn(warning)\n\t\t\thasWarnings = true\n\t\t}\n\n\t\tflatRoutes = flattenRoutes(parseResult.routes)\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\tlogger.errorWithHelp(ERRORS.ROUTES_FILE_PARSE_ERROR(routesFile, message))\n\t\tprocess.exit(1)\n\t}\n\n\tif (flatRoutes.length === 0) {\n\t\tlogger.warn(\"No routes found in the config file\")\n\t\treturn hasWarnings\n\t}\n\n\t// Find all screen.meta.ts files\n\tconst metaFiles = await glob(config.metaPattern, {\n\t\tcwd,\n\t\tignore: config.ignore,\n\t})\n\n\t// Build a set of directories that have screen.meta.ts\n\tconst metaDirs = new Set<string>()\n\t// Also build a map from directory basename to full path for component name matching\n\tconst metaDirsByName = new Map<string, string>()\n\tfor (const metaFile of metaFiles) {\n\t\tconst dir = dirname(metaFile)\n\t\tmetaDirs.add(dir)\n\t\t// Store lowercase basename for case-insensitive matching\n\t\tconst baseName = dir.split(\"/\").pop()?.toLowerCase() || \"\"\n\t\tif (baseName) {\n\t\t\tmetaDirsByName.set(baseName, dir)\n\t\t}\n\t}\n\n\t// Check each route for screen.meta.ts coverage\n\tconst missingMeta: FlatRoute[] = []\n\tconst covered: FlatRoute[] = []\n\n\tfor (const route of flatRoutes) {\n\t\t// Skip layout routes (components ending with \"Layout\" that typically don't need screen.meta)\n\t\tif (route.componentPath?.endsWith(\"Layout\")) {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Try multiple matching strategies\n\t\tlet matched = false\n\n\t\t// Strategy 1: Check by determineMetaDir (path-based matching)\n\t\tconst metaPath = determineMetaDir(route, cwd)\n\t\tif (metaDirs.has(metaPath)) {\n\t\t\tmatched = true\n\t\t}\n\n\t\t// Strategy 2: Match by component name (for React Router)\n\t\tif (!matched && route.componentPath) {\n\t\t\tconst componentName = route.componentPath.toLowerCase()\n\t\t\tif (metaDirsByName.has(componentName)) {\n\t\t\t\tmatched = true\n\t\t\t}\n\n\t\t\t// Also try matching the last word of component name\n\t\t\t// e.g., \"UserProfile\" -> check for \"profile\" directory\n\t\t\tif (!matched) {\n\t\t\t\t// Split by uppercase letters to get parts\n\t\t\t\tconst parts = route.componentPath.split(/(?=[A-Z])/)\n\t\t\t\tconst lastPart = parts[parts.length - 1]\n\t\t\t\tif (parts.length > 1 && lastPart) {\n\t\t\t\t\tif (metaDirsByName.has(lastPart.toLowerCase())) {\n\t\t\t\t\t\tmatched = true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Strategy 3: Match by screenId path pattern\n\t\tif (!matched) {\n\t\t\tconst screenPath = route.screenId.replace(/\\./g, \"/\")\n\t\t\tfor (const dir of metaDirs) {\n\t\t\t\tif (dir.endsWith(screenPath)) {\n\t\t\t\t\tmatched = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (matched) {\n\t\t\tcovered.push(route)\n\t\t} else {\n\t\t\tmissingMeta.push(route)\n\t\t}\n\t}\n\n\t// Report results\n\tconst total = covered.length + missingMeta.length\n\tconst coveredCount = covered.length\n\tconst missingCount = missingMeta.length\n\tconst coveragePercent = Math.round((coveredCount / total) * 100)\n\n\tlogger.log(`Found ${total} routes`)\n\tlogger.log(`Coverage: ${coveredCount}/${total} (${coveragePercent}%)`)\n\tlogger.blank()\n\n\t// Determine if lint should fail\n\tconst minimumCoverage = adoption.minimumCoverage ?? 100\n\tconst passedCoverage = coveragePercent >= minimumCoverage\n\n\tif (missingCount > 0) {\n\t\tlogger.log(`Missing screen.meta.ts (${missingCount} routes):`)\n\t\tlogger.blank()\n\n\t\tfor (const route of missingMeta) {\n\t\t\tconst suggestedMetaPath = determineSuggestedMetaPath(route, cwd)\n\t\t\tlogger.itemError(\n\t\t\t\t`${route.fullPath} ${logger.dim(`(${route.screenId})`)}`,\n\t\t\t)\n\t\t\tlogger.log(` ${logger.dim(\"→\")} ${logger.path(suggestedMetaPath)}`)\n\t\t}\n\n\t\tlogger.blank()\n\t}\n\n\tif (!passedCoverage) {\n\t\tlogger.error(\n\t\t\t`Lint failed: Coverage ${coveragePercent}% is below minimum ${minimumCoverage}%`,\n\t\t)\n\t\tprocess.exit(1)\n\t} else if (missingCount > 0) {\n\t\tlogger.success(\n\t\t\t`Coverage ${coveragePercent}% meets minimum ${minimumCoverage}%`,\n\t\t)\n\t\tif (adoption.mode === \"progressive\") {\n\t\t\tlogger.log(\n\t\t\t\t` ${logger.dim(\"Tip:\")} Increase minimumCoverage in config to gradually improve coverage`,\n\t\t\t)\n\t\t}\n\t} else {\n\t\tlogger.done(\"All routes have screen.meta.ts files\")\n\t}\n\n\t// Check for orphan screens and cycles using screens.json\n\tconst screensPath = join(cwd, config.outDir, \"screens.json\")\n\tif (existsSync(screensPath)) {\n\t\ttry {\n\t\t\tconst content = readFileSync(screensPath, \"utf-8\")\n\t\t\tconst screens = JSON.parse(content) as Screen[]\n\n\t\t\t// Check for orphan screens\n\t\t\tconst orphans = findOrphanScreens(screens)\n\n\t\t\tif (orphans.length > 0) {\n\t\t\t\thasWarnings = true\n\t\t\t\tlogger.blank()\n\t\t\t\tlogger.warn(`Orphan screens detected (${orphans.length}):`)\n\t\t\t\tlogger.blank()\n\t\t\t\tlogger.log(\" These screens have no entryPoints and are not\")\n\t\t\t\tlogger.log(\" referenced in any other screen's 'next' array.\")\n\t\t\t\tlogger.blank()\n\t\t\t\tfor (const orphan of orphans) {\n\t\t\t\t\tlogger.itemWarn(`${orphan.id} ${logger.dim(orphan.route)}`)\n\t\t\t\t}\n\t\t\t\tlogger.blank()\n\t\t\t\tlogger.log(\n\t\t\t\t\t` ${logger.dim(\"Consider adding entryPoints or removing these screens.\")}`,\n\t\t\t\t)\n\t\t\t}\n\n\t\t\t// Check for circular navigation\n\t\t\tif (!allowCycles) {\n\t\t\t\tconst cycleResult = detectCycles(screens)\n\t\t\t\tif (cycleResult.hasCycles) {\n\t\t\t\t\thasWarnings = true\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tlogger.warn(getCycleSummary(cycleResult))\n\t\t\t\t\tlogger.log(formatCycleWarnings(cycleResult.cycles))\n\t\t\t\t\tlogger.blank()\n\t\t\t\t\tif (cycleResult.disallowedCycles.length > 0) {\n\t\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t\t` ${logger.dim(\"Use 'allowCycles: true' in screen.meta.ts to allow intentional cycles.\")}`,\n\t\t\t\t\t\t)\n\n\t\t\t\t\t\tif (strict) {\n\t\t\t\t\t\t\tlogger.blank()\n\t\t\t\t\t\t\tlogger.errorWithHelp(\n\t\t\t\t\t\t\t\tERRORS.CYCLES_DETECTED(cycleResult.disallowedCycles.length),\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\tprocess.exit(1)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Check for invalid navigation references\n\t\t\tconst invalidNavs = findInvalidNavigations(screens)\n\t\t\tif (invalidNavs.length > 0) {\n\t\t\t\thasWarnings = true\n\t\t\t\tlogger.blank()\n\t\t\t\tlogger.warn(`Invalid navigation targets (${invalidNavs.length}):`)\n\t\t\t\tlogger.blank()\n\t\t\t\tlogger.log(\n\t\t\t\t\t\" These navigation references point to non-existent screens.\",\n\t\t\t\t)\n\t\t\t\tlogger.blank()\n\t\t\t\tfor (const inv of invalidNavs) {\n\t\t\t\t\tlogger.itemWarn(\n\t\t\t\t\t\t`${inv.screenId} → ${logger.dim(inv.field)}: \"${inv.target}\"`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tlogger.blank()\n\t\t\t\tlogger.log(\n\t\t\t\t\t` ${logger.dim(\"Check that these screen IDs exist in your codebase.\")}`,\n\t\t\t\t)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (error instanceof SyntaxError) {\n\t\t\t\tlogger.warn(\"Failed to parse screens.json - file may be corrupted\")\n\t\t\t\tlogger.log(` ${logger.dim(\"Run 'screenbook build' to regenerate.\")}`)\n\t\t\t\thasWarnings = true\n\t\t\t} else if (error instanceof Error) {\n\t\t\t\tlogger.warn(`Failed to analyze screens.json: ${error.message}`)\n\t\t\t\thasWarnings = true\n\t\t\t} else {\n\t\t\t\tlogger.warn(`Failed to analyze screens.json: ${String(error)}`)\n\t\t\t\thasWarnings = true\n\t\t\t}\n\t\t}\n\t}\n\n\tif (hasWarnings) {\n\t\tlogger.blank()\n\t\tlogger.warn(\"Lint completed with warnings.\")\n\t}\n\n\treturn hasWarnings\n}\n\n/**\n * Determine the directory where screen.meta.ts should be for a route\n */\nfunction determineMetaDir(route: FlatRoute, cwd: string): string {\n\t// If component path is available, check relative to component directory\n\tif (route.componentPath) {\n\t\tconst componentDir = dirname(route.componentPath)\n\t\tconst relativePath = relative(cwd, componentDir)\n\t\t// Ensure path doesn't escape cwd\n\t\tif (!relativePath.startsWith(\"..\")) {\n\t\t\treturn relativePath\n\t\t}\n\t}\n\n\t// Fall back to src/screens/{screenId} convention\n\tconst screenDir = route.screenId.replace(/\\./g, \"/\")\n\treturn join(\"src\", \"screens\", screenDir)\n}\n\n/**\n * Determine the suggested screen.meta.ts path for a route\n */\nfunction determineSuggestedMetaPath(route: FlatRoute, cwd: string): string {\n\tconst metaDir = determineMetaDir(route, cwd)\n\treturn join(metaDir, \"screen.meta.ts\")\n}\n\ninterface InvalidNavigation {\n\tscreenId: string\n\tfield: string\n\ttarget: string\n}\n\n/**\n * Find navigation references that point to non-existent screens.\n * Checks `next`, `entryPoints` arrays and mock navigation targets.\n */\nfunction findInvalidNavigations(screens: Screen[]): InvalidNavigation[] {\n\tconst screenIds = new Set(screens.map((s) => s.id))\n\tconst invalid: InvalidNavigation[] = []\n\n\tfor (const screen of screens) {\n\t\t// Check next array\n\t\tif (screen.next) {\n\t\t\tfor (const target of screen.next) {\n\t\t\t\tif (!screenIds.has(target)) {\n\t\t\t\t\tinvalid.push({ screenId: screen.id, field: \"next\", target })\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Check entryPoints array\n\t\tif (screen.entryPoints) {\n\t\t\tfor (const target of screen.entryPoints) {\n\t\t\t\tif (!screenIds.has(target)) {\n\t\t\t\t\tinvalid.push({ screenId: screen.id, field: \"entryPoints\", target })\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn invalid\n}\n\n/**\n * Find screens that are unreachable (orphans).\n * A screen is an orphan if:\n * - It has no entryPoints defined\n * - AND it's not referenced in any other screen's `next` array\n */\nfunction findOrphanScreens(screens: Screen[]): Screen[] {\n\t// Build a set of all screen IDs that are referenced in `next` arrays\n\tconst referencedIds = new Set<string>()\n\tfor (const screen of screens) {\n\t\tif (screen.next) {\n\t\t\tfor (const nextId of screen.next) {\n\t\t\t\treferencedIds.add(nextId)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Find orphan screens\n\tconst orphans: Screen[] = []\n\tfor (const screen of screens) {\n\t\tconst hasEntryPoints = screen.entryPoints && screen.entryPoints.length > 0\n\t\tconst isReferenced = referencedIds.has(screen.id)\n\n\t\t// A screen is an orphan if it has no entry points AND is not referenced\n\t\tif (!hasEntryPoints && !isReferenced) {\n\t\t\torphans.push(screen)\n\t\t}\n\t}\n\n\treturn orphans\n}\n","import { basename, dirname } from \"node:path\"\nimport type { ImpactResult } from \"./impactAnalysis.js\"\n\n/**\n * Extract potential API names from changed file paths.\n * Looks for common API file patterns.\n */\nexport function extractApiNames(files: string[]): string[] {\n\tconst apis = new Set<string>()\n\n\tfor (const file of files) {\n\t\tconst fileName = basename(file, \".ts\")\n\t\t\t.replace(/\\.tsx?$/, \"\")\n\t\t\t.replace(/\\.js$/, \"\")\n\t\t\t.replace(/\\.jsx?$/, \"\")\n\n\t\tconst dirName = basename(dirname(file))\n\n\t\t// Pattern: src/api/InvoiceAPI.ts -> InvoiceAPI\n\t\tif (\n\t\t\tfile.includes(\"/api/\") ||\n\t\t\tfile.includes(\"/apis/\") ||\n\t\t\tfile.includes(\"/services/\")\n\t\t) {\n\t\t\tif (\n\t\t\t\tfileName.endsWith(\"API\") ||\n\t\t\t\tfileName.endsWith(\"Api\") ||\n\t\t\t\tfileName.endsWith(\"Service\")\n\t\t\t) {\n\t\t\t\tapis.add(fileName)\n\t\t\t}\n\t\t}\n\n\t\t// Pattern: src/services/invoice/index.ts -> InvoiceService\n\t\tif (\n\t\t\tfile.includes(\"/services/\") &&\n\t\t\t(fileName === \"index\" || fileName === dirName)\n\t\t) {\n\t\t\tconst serviceName = `${capitalize(dirName)}Service`\n\t\t\tapis.add(serviceName)\n\t\t}\n\n\t\t// Pattern: src/api/invoice.ts -> InvoiceAPI\n\t\tif (file.includes(\"/api/\") || file.includes(\"/apis/\")) {\n\t\t\tif (!fileName.endsWith(\"API\") && !fileName.endsWith(\"Api\")) {\n\t\t\t\tconst apiName = `${capitalize(fileName)}API`\n\t\t\t\tapis.add(apiName)\n\t\t\t}\n\t\t}\n\n\t\t// Pattern: explicit API/Service file names\n\t\tif (\n\t\t\tfileName.toLowerCase().includes(\"api\") ||\n\t\t\tfileName.toLowerCase().includes(\"service\")\n\t\t) {\n\t\t\tapis.add(fileName)\n\t\t}\n\t}\n\n\treturn Array.from(apis).sort()\n}\n\nexport function capitalize(str: string): string {\n\treturn str.charAt(0).toUpperCase() + str.slice(1)\n}\n\n/**\n * Format the results as Markdown for PR comments.\n */\nexport function formatMarkdown(\n\tchangedFiles: string[],\n\tdetectedApis: string[],\n\tresults: ImpactResult[],\n): string {\n\tconst lines: string[] = []\n\n\tlines.push(\"## Screenbook Impact Analysis\")\n\tlines.push(\"\")\n\n\tif (results.length === 0) {\n\t\tlines.push(\"No screen impacts detected from the API changes in this PR.\")\n\t\tlines.push(\"\")\n\t\tlines.push(\"<details>\")\n\t\tlines.push(\"<summary>Detected APIs (no screen dependencies)</summary>\")\n\t\tlines.push(\"\")\n\t\tfor (const api of detectedApis) {\n\t\t\tlines.push(`- \\`${api}\\``)\n\t\t}\n\t\tlines.push(\"\")\n\t\tlines.push(\"</details>\")\n\t\treturn lines.join(\"\\n\")\n\t}\n\n\t// Summary\n\tconst totalDirect = results.reduce((sum, r) => sum + r.direct.length, 0)\n\tconst totalTransitive = results.reduce(\n\t\t(sum, r) => sum + r.transitive.length,\n\t\t0,\n\t)\n\tconst totalScreens = totalDirect + totalTransitive\n\n\tlines.push(\n\t\t`**${totalScreens} screen${totalScreens > 1 ? \"s\" : \"\"} affected** by changes to ${results.length} API${results.length > 1 ? \"s\" : \"\"}`,\n\t)\n\tlines.push(\"\")\n\n\t// Per-API breakdown\n\tfor (const result of results) {\n\t\tlines.push(`### ${result.api}`)\n\t\tlines.push(\"\")\n\n\t\tif (result.direct.length > 0) {\n\t\t\tlines.push(`**Direct dependencies** (${result.direct.length}):`)\n\t\t\tlines.push(\"\")\n\t\t\tlines.push(\"| Screen | Route | Owner |\")\n\t\t\tlines.push(\"|--------|-------|-------|\")\n\t\t\tfor (const screen of result.direct) {\n\t\t\t\tconst owner = screen.owner?.join(\", \") ?? \"-\"\n\t\t\t\tlines.push(`| ${screen.id} | \\`${screen.route}\\` | ${owner} |`)\n\t\t\t}\n\t\t\tlines.push(\"\")\n\t\t}\n\n\t\tif (result.transitive.length > 0) {\n\t\t\tlines.push(`**Transitive dependencies** (${result.transitive.length}):`)\n\t\t\tlines.push(\"\")\n\t\t\tfor (const { path } of result.transitive) {\n\t\t\t\tlines.push(`- ${path.join(\" → \")}`)\n\t\t\t}\n\t\t\tlines.push(\"\")\n\t\t}\n\t}\n\n\t// Changed files summary\n\tlines.push(\"<details>\")\n\tlines.push(`<summary>Changed files (${changedFiles.length})</summary>`)\n\tlines.push(\"\")\n\tfor (const file of changedFiles.slice(0, 20)) {\n\t\tlines.push(`- \\`${file}\\``)\n\t}\n\tif (changedFiles.length > 20) {\n\t\tlines.push(`- ... and ${changedFiles.length - 20} more`)\n\t}\n\tlines.push(\"\")\n\tlines.push(\"</details>\")\n\n\treturn lines.join(\"\\n\")\n}\n","import { execSync } from \"node:child_process\"\nimport { existsSync, readFileSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport type { Screen } from \"@screenbook/core\"\nimport { define } from \"gunshi\"\nimport { loadConfig } from \"../utils/config.js\"\nimport { ERRORS } from \"../utils/errors.js\"\nimport { analyzeImpact, type ImpactResult } from \"../utils/impactAnalysis.js\"\nimport { logger } from \"../utils/logger.js\"\nimport { extractApiNames, formatMarkdown } from \"../utils/prImpact.js\"\n\nexport const prImpactCommand = define({\n\tname: \"pr-impact\",\n\tdescription: \"Analyze impact of changed files in a PR\",\n\targs: {\n\t\tbase: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"b\",\n\t\t\tdescription: \"Base branch to compare against (default: main)\",\n\t\t\tdefault: \"main\",\n\t\t},\n\t\tconfig: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"c\",\n\t\t\tdescription: \"Path to config file\",\n\t\t},\n\t\tformat: {\n\t\t\ttype: \"string\",\n\t\t\tshort: \"f\",\n\t\t\tdescription: \"Output format: markdown (default) or json\",\n\t\t\tdefault: \"markdown\",\n\t\t},\n\t\tdepth: {\n\t\t\ttype: \"number\",\n\t\t\tshort: \"d\",\n\t\t\tdescription: \"Maximum depth for transitive dependencies\",\n\t\t\tdefault: 3,\n\t\t},\n\t},\n\trun: async (ctx) => {\n\t\tconst config = await loadConfig(ctx.values.config)\n\t\tconst cwd = process.cwd()\n\n\t\tconst baseBranch = ctx.values.base ?? \"main\"\n\t\tconst format = ctx.values.format ?? \"markdown\"\n\t\tconst depth = ctx.values.depth ?? 3\n\n\t\t// Get changed files from git\n\t\tlet changedFiles: string[]\n\t\ttry {\n\t\t\tconst gitOutput = execSync(\n\t\t\t\t`git diff --name-only ${baseBranch}...HEAD 2>/dev/null || git diff --name-only ${baseBranch} HEAD`,\n\t\t\t\t{ cwd, encoding: \"utf-8\" },\n\t\t\t)\n\t\t\tchangedFiles = gitOutput\n\t\t\t\t.split(\"\\n\")\n\t\t\t\t.map((f) => f.trim())\n\t\t\t\t.filter((f) => f.length > 0)\n\t\t} catch {\n\t\t\tlogger.errorWithHelp(ERRORS.GIT_CHANGED_FILES_ERROR(baseBranch))\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tif (changedFiles.length === 0) {\n\t\t\tlogger.info(\"No changed files found.\")\n\t\t\treturn\n\t\t}\n\n\t\t// Extract potential API names from changed files\n\t\tconst apiNames = extractApiNames(changedFiles)\n\n\t\tif (apiNames.length === 0) {\n\t\t\tif (format === \"markdown\") {\n\t\t\t\tlogger.log(\"## Screenbook Impact Analysis\")\n\t\t\t\tlogger.blank()\n\t\t\t\tlogger.log(\"No API-related changes detected in this PR.\")\n\t\t\t\tlogger.blank()\n\t\t\t\tlogger.log(`Changed files: ${changedFiles.length}`)\n\t\t\t} else {\n\t\t\t\tlogger.log(\n\t\t\t\t\tJSON.stringify({ apis: [], results: [], changedFiles }, null, 2),\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\t// Load screens.json\n\t\tconst screensPath = join(cwd, config.outDir, \"screens.json\")\n\n\t\tif (!existsSync(screensPath)) {\n\t\t\tlogger.errorWithHelp(ERRORS.SCREENS_NOT_FOUND)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tlet screens: Screen[]\n\t\ttry {\n\t\t\tconst content = readFileSync(screensPath, \"utf-8\")\n\t\t\tscreens = JSON.parse(content) as Screen[]\n\t\t} catch (error) {\n\t\t\tlogger.errorWithHelp({\n\t\t\t\t...ERRORS.SCREENS_PARSE_ERROR,\n\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t})\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\t// Analyze impact for each API\n\t\tconst results: ImpactResult[] = []\n\t\tfor (const apiName of apiNames) {\n\t\t\tconst result = analyzeImpact(screens, apiName, depth)\n\t\t\tif (result.totalCount > 0) {\n\t\t\t\tresults.push(result)\n\t\t\t}\n\t\t}\n\n\t\t// Output results\n\t\tif (format === \"json\") {\n\t\t\tlogger.log(\n\t\t\t\tJSON.stringify(\n\t\t\t\t\t{\n\t\t\t\t\t\tchangedFiles,\n\t\t\t\t\t\tdetectedApis: apiNames,\n\t\t\t\t\t\tresults: results.map((r) => ({\n\t\t\t\t\t\t\tapi: r.api,\n\t\t\t\t\t\t\tdirectCount: r.direct.length,\n\t\t\t\t\t\t\ttransitiveCount: r.transitive.length,\n\t\t\t\t\t\t\ttotalCount: r.totalCount,\n\t\t\t\t\t\t\tdirect: r.direct.map((s) => ({\n\t\t\t\t\t\t\t\tid: s.id,\n\t\t\t\t\t\t\t\ttitle: s.title,\n\t\t\t\t\t\t\t\troute: s.route,\n\t\t\t\t\t\t\t\towner: s.owner,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t\ttransitive: r.transitive.map(({ screen, path }) => ({\n\t\t\t\t\t\t\t\tid: screen.id,\n\t\t\t\t\t\t\t\ttitle: screen.title,\n\t\t\t\t\t\t\t\troute: screen.route,\n\t\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t})),\n\t\t\t\t\t},\n\t\t\t\t\tnull,\n\t\t\t\t\t2,\n\t\t\t\t),\n\t\t\t)\n\t\t} else {\n\t\t\tlogger.log(formatMarkdown(changedFiles, apiNames, results))\n\t\t}\n\t},\n})\n","#!/usr/bin/env node\n\nimport { readFileSync } from \"node:fs\"\nimport { dirname, join } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\nimport { cli, define } from \"gunshi\"\nimport { buildCommand } from \"./commands/build.js\"\nimport { devCommand } from \"./commands/dev.js\"\nimport { doctorCommand } from \"./commands/doctor.js\"\nimport { generateCommand } from \"./commands/generate.js\"\nimport { impactCommand } from \"./commands/impact.js\"\nimport { initCommand } from \"./commands/init.js\"\nimport { lintCommand } from \"./commands/lint.js\"\nimport { prImpactCommand } from \"./commands/pr-impact.js\"\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nconst packageJson = JSON.parse(\n\treadFileSync(join(__dirname, \"..\", \"package.json\"), \"utf-8\"),\n)\nconst version: string = packageJson.version\n\nconst mainCommand = define({\n\tname: \"screenbook\",\n\tdescription: \"Screen catalog and navigation graph generator\",\n\trun: () => {\n\t\tconsole.log(\"Usage: screenbook <command>\")\n\t\tconsole.log(\"\")\n\t\tconsole.log(\"Commands:\")\n\t\tconsole.log(\" init Initialize Screenbook in a project\")\n\t\tconsole.log(\" generate Auto-generate screen.meta.ts from routes\")\n\t\tconsole.log(\" build Build screen metadata JSON\")\n\t\tconsole.log(\" dev Start the development server\")\n\t\tconsole.log(\" lint Detect routes without screen.meta\")\n\t\tconsole.log(\" impact Analyze API dependency impact\")\n\t\tconsole.log(\" pr-impact Analyze PR changes impact\")\n\t\tconsole.log(\" doctor Diagnose common setup issues\")\n\t\tconsole.log(\"\")\n\t\tconsole.log(\"Run 'screenbook <command> --help' for more information\")\n\t},\n})\n\nawait cli(process.argv.slice(2), mainCommand, {\n\tname: \"screenbook\",\n\tversion,\n\tsubCommands: {\n\t\tinit: initCommand,\n\t\tgenerate: generateCommand,\n\t\tbuild: buildCommand,\n\t\tdev: devCommand,\n\t\tlint: lintCommand,\n\t\timpact: impactCommand,\n\t\t\"pr-impact\": prImpactCommand,\n\t\tdoctor: doctorCommand,\n\t},\n})\n"],"mappings":"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"}