@screenbook/cli 1.1.2 → 1.1.3

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.
package/dist/index.mjs CHANGED
@@ -733,13 +733,19 @@ async function checkDependencies(cwd) {
733
733
  ...pkg.dependencies,
734
734
  ...pkg.devDependencies
735
735
  };
736
+ const unifiedVersion = allDeps.screenbook;
736
737
  const coreVersion = allDeps["@screenbook/core"];
737
738
  const cliVersion = allDeps["@screenbook/cli"];
739
+ if (unifiedVersion) return {
740
+ name: "Dependencies",
741
+ status: "pass",
742
+ message: `screenbook@${unifiedVersion}`
743
+ };
738
744
  if (!coreVersion && !cliVersion) return {
739
745
  name: "Dependencies",
740
746
  status: "fail",
741
747
  message: "Screenbook packages not installed",
742
- suggestion: "Run 'pnpm add -D @screenbook/core @screenbook/cli' to install"
748
+ suggestion: "Run 'pnpm add -D screenbook' or 'pnpm add -D @screenbook/core @screenbook/cli' to install"
743
749
  };
744
750
  if (!coreVersion) return {
745
751
  name: "Dependencies",
@@ -864,8 +870,14 @@ async function checkVersionCompatibility(cwd) {
864
870
  ...pkg.dependencies,
865
871
  ...pkg.devDependencies
866
872
  };
873
+ const unifiedVersion = allDeps.screenbook;
867
874
  const coreVersion = allDeps["@screenbook/core"];
868
875
  const cliVersion = allDeps["@screenbook/cli"];
876
+ if (unifiedVersion) return {
877
+ name: "Version compatibility",
878
+ status: "pass",
879
+ message: "Using unified screenbook package"
880
+ };
869
881
  if (!coreVersion || !cliVersion) return {
870
882
  name: "Version compatibility",
871
883
  status: "warn",
@@ -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 coreVersion = allDeps[\"@screenbook/core\"]\n\t\tconst cliVersion = allDeps[\"@screenbook/cli\"]\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/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 coreVersion = allDeps[\"@screenbook/core\"]\n\t\tconst cliVersion = allDeps[\"@screenbook/cli\"]\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[]","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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@screenbook/cli",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "description": "CLI for Screenbook - screen catalog and navigation graph generator",
5
5
  "type": "module",
6
6
  "bin": {