@screenbook/cli 0.0.1
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/LICENSE +21 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.mjs +1262 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["screens: Screen[]","routeFiles: string[]","missing: CoverageData[\"missing\"]","byOwner: CoverageData[\"byOwner\"]","byTag: CoverageData[\"byTag\"]","lines: string[]","screens: Screen[]","transitive: TransitiveDependency[]","queue: Array<{ id: string; path: string[] }>","lines: string[]","screens: Screen[]","FRAMEWORKS: FrameworkDefinition[]","framework: FrameworkInfo | null","missingMeta: string[]","covered: string[]","orphans: Screen[]","changedFiles: string[]","screens: Screen[]","results: ImpactResult[]","lines: string[]"],"sources":["../src/utils/config.ts","../src/commands/build.ts","../src/commands/dev.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/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 { 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\"\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},\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\tconsole.log(\"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\tconsole.log(\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\tconsole.log(`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// Load and collect screen metadata\n\t\tconst screens: Screen[] = []\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)\n\t\t\t\t\tconsole.log(` ✓ ${module.screen.id}`)\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(` ✗ Failed to load ${file}:`, error)\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\tconsole.log(`\\nGenerated ${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\tconsole.log(`Generated ${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\tconsole.log(`Generated ${coveragePath}`)\n\t\tconsole.log(\n\t\t\t`\\nCoverage: ${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\"\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\tconsole.log(\"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\tconsole.error(\"Could not find @screenbook/ui package\")\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\tconsole.log(`\\nStarting UI server on http://localhost:${port}`)\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\tconsole.error(\"Failed to start Astro server:\", error)\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\tconsole.log(`No screen.meta.ts files found matching: ${config.metaPattern}`)\n\t\treturn\n\t}\n\n\tconsole.log(`Found ${files.length} screen files`)\n\n\tconst jiti = createJiti(cwd)\n\tconst screens: Screen[] = []\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)\n\t\t\t\tconsole.log(` ✓ ${module.screen.id}`)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error(` ✗ Failed to load ${file}:`, error)\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\tconsole.log(`\\nGenerated ${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, writeFileSync } from \"node:fs\"\nimport { basename, dirname, join, relative } from \"node:path\"\nimport { define } from \"gunshi\"\nimport { glob } from \"tinyglobby\"\nimport { loadConfig } from \"../utils/config.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},\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\n\t\tif (!config.routesPattern) {\n\t\t\tconsole.log(\"Error: routesPattern not configured\")\n\t\t\tconsole.log(\"\")\n\t\t\tconsole.log(\"Add routesPattern to your screenbook.config.ts:\")\n\t\t\tconsole.log(\"\")\n\t\t\tconsole.log(' routesPattern: \"src/pages/**/page.tsx\", // Vite/React')\n\t\t\tconsole.log(\n\t\t\t\t' routesPattern: \"app/**/page.tsx\", // Next.js App Router',\n\t\t\t)\n\t\t\tconsole.log(' routesPattern: \"src/pages/**/*.vue\", // Vue/Nuxt')\n\t\t\tconsole.log(\"\")\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tconsole.log(\"Scanning for route files...\")\n\t\tconsole.log(\"\")\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\tconsole.log(`No route files found matching: ${config.routesPattern}`)\n\t\t\treturn\n\t\t}\n\n\t\tconsole.log(`Found ${routeFiles.length} route files`)\n\t\tconsole.log(\"\")\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\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\t\t\tconst content = generateScreenMetaContent(screenMeta)\n\n\t\t\tif (dryRun) {\n\t\t\t\tconsole.log(`Would create: ${metaPath}`)\n\t\t\t\tconsole.log(` id: \"${screenMeta.id}\"`)\n\t\t\t\tconsole.log(` title: \"${screenMeta.title}\"`)\n\t\t\t\tconsole.log(` route: \"${screenMeta.route}\"`)\n\t\t\t\tconsole.log(\"\")\n\t\t\t} else {\n\t\t\t\twriteFileSync(absoluteMetaPath, content)\n\t\t\t\tconsole.log(`✓ Created: ${metaPath}`)\n\t\t\t}\n\n\t\t\tcreated++\n\t\t}\n\n\t\tconsole.log(\"\")\n\t\tif (dryRun) {\n\t\t\tconsole.log(`Would create ${created} files (${skipped} already exist)`)\n\t\t\tconsole.log(\"\")\n\t\t\tconsole.log(\"Run without --dry-run to create files\")\n\t\t} else {\n\t\t\tconsole.log(`Created ${created} files (${skipped} skipped)`)\n\t\t\tif (created > 0) {\n\t\t\t\tconsole.log(\"\")\n\t\t\t\tconsole.log(\"Next steps:\")\n\t\t\t\tconsole.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\tconsole.log(\" 2. Run 'screenbook dev' to view your screen catalog\")\n\t\t\t}\n\t\t}\n\t},\n})\n\ninterface InferredScreenMeta {\n\tid: string\n\ttitle: string\n\troute: string\n}\n\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\n/**\n * Generate screen.meta.ts file content\n */\nfunction generateScreenMetaContent(meta: InferredScreenMeta): string {\n\t// Infer a tag from the first segment of the ID\n\tconst inferredTag = meta.id.split(\".\")[0] || \"general\"\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: [],\n\n\t// Tags for filtering in the catalog\n\ttags: [\"${inferredTag}\"],\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\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 { screen, 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 {\n\tanalyzeImpact,\n\tformatImpactJson,\n\tformatImpactText,\n} from \"../utils/impactAnalysis.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\tconsole.error(\"Error: API name is required\")\n\t\t\tconsole.error(\"\")\n\t\t\tconsole.error(\"Usage: screenbook impact <api-name>\")\n\t\t\tconsole.error(\"\")\n\t\t\tconsole.error(\"Examples:\")\n\t\t\tconsole.error(\" screenbook impact InvoiceAPI.getDetail\")\n\t\t\tconsole.error(\" screenbook impact PaymentService\")\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\tconsole.error(\"Error: screens.json not found\")\n\t\t\tconsole.error(\"\")\n\t\t\tconsole.error(\n\t\t\t\t\"Run 'screenbook build' first to generate the screen catalog.\",\n\t\t\t)\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\tconsole.error(\"Error: Failed to read screens.json\")\n\t\t\tconsole.error(error instanceof Error ? error.message : String(error))\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tif (screens.length === 0) {\n\t\t\tconsole.log(\"No screens found in the catalog.\")\n\t\t\tconsole.log(\"\")\n\t\t\tconsole.log(\"Run 'screenbook generate' to create screen.meta.ts files,\")\n\t\t\tconsole.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\tconsole.log(formatImpactJson(result))\n\t\t} else {\n\t\t\tconsole.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\"\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\tconsole.log(\"\")\n\tconsole.log(\"What Screenbook gives you:\")\n\tconsole.log(\" - Screen catalog with search & filter\")\n\tconsole.log(\" - Navigation graph visualization\")\n\tconsole.log(\" - Impact analysis (API -> affected screens)\")\n\tconsole.log(\" - CI lint for documentation coverage\")\n}\n\nfunction printNextSteps(hasRoutesPattern: boolean): void {\n\tconsole.log(\"\")\n\tconsole.log(\"Next steps:\")\n\tif (hasRoutesPattern) {\n\t\tconsole.log(\n\t\t\t\" 1. Run 'screenbook generate' to auto-create screen.meta.ts files\",\n\t\t)\n\t\tconsole.log(\" 2. Run 'screenbook dev' to start the UI server\")\n\t} else {\n\t\tconsole.log(\" 1. Configure routesPattern in screenbook.config.ts\")\n\t\tconsole.log(\n\t\t\t\" 2. Run 'screenbook generate' to auto-create screen.meta.ts files\",\n\t\t)\n\t\tconsole.log(\" 3. Run 'screenbook dev' to start the UI server\")\n\t}\n\tconsole.log(\"\")\n\tconsole.log(\"screen.meta.ts files are created alongside your route files:\")\n\tconsole.log(\"\")\n\tconsole.log(\" src/pages/dashboard/\")\n\tconsole.log(\" page.tsx # Your route file\")\n\tconsole.log(\" screen.meta.ts # Auto-generated, customize as needed\")\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\tconsole.log(\"Initializing Screenbook...\")\n\t\tconsole.log(\"\")\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\tconsole.log(` Detected: ${framework.name}`)\n\t\t\t} else {\n\t\t\t\tconsole.log(\" Could not auto-detect framework\")\n\t\t\t\tconsole.log(\"\")\n\t\t\t\tframework = await promptFrameworkSelection()\n\n\t\t\t\tif (framework) {\n\t\t\t\t\tconsole.log(\"\")\n\t\t\t\t\tconsole.log(` 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\tconsole.log(\" - screenbook.config.ts already exists (skipped)\")\n\t\t} else {\n\t\t\tconst configContent = generateConfigTemplate(framework)\n\t\t\twriteFileSync(configPath, configContent)\n\t\t\tconsole.log(\" + 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\tconsole.log(\" + Added .screenbook to .gitignore\")\n\t\t\t} else {\n\t\t\t\tconsole.log(\" - .screenbook already in .gitignore (skipped)\")\n\t\t\t}\n\t\t} else {\n\t\t\twriteFileSync(gitignorePath, `# Screenbook\\n${screenbookIgnore}\\n`)\n\t\t\tconsole.log(\" + Created .gitignore with .screenbook\")\n\t\t}\n\n\t\tconsole.log(\"\")\n\t\tconsole.log(\"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\"\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},\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\tconsole.log(\"Error: routesPattern not configured\")\n\t\t\tconsole.log(\"\")\n\t\t\tconsole.log(\"Add routesPattern to your screenbook.config.ts:\")\n\t\t\tconsole.log(\"\")\n\t\t\tconsole.log(' routesPattern: \"src/pages/**/page.tsx\", // Vite/React')\n\t\t\tconsole.log(\n\t\t\t\t' routesPattern: \"app/**/page.tsx\", // Next.js App Router',\n\t\t\t)\n\t\t\tconsole.log(' routesPattern: \"src/pages/**/*.vue\", // Vue/Nuxt')\n\t\t\tconsole.log(\"\")\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tconsole.log(\"Linting screen metadata coverage...\")\n\t\tif (adoption.mode === \"progressive\") {\n\t\t\tconsole.log(`Mode: Progressive adoption`)\n\t\t\tif (adoption.includePatterns?.length) {\n\t\t\t\tconsole.log(`Checking: ${adoption.includePatterns.join(\", \")}`)\n\t\t\t}\n\t\t\tif (adoption.minimumCoverage != null) {\n\t\t\t\tconsole.log(`Minimum coverage: ${adoption.minimumCoverage}%`)\n\t\t\t}\n\t\t}\n\t\tconsole.log(\"\")\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\tconsole.log(`No route files found matching: ${config.routesPattern}`)\n\t\t\tif (adoption.mode === \"progressive\" && adoption.includePatterns?.length) {\n\t\t\t\tconsole.log(\n\t\t\t\t\t`(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\tconsole.log(`Found ${total} route files`)\n\t\tconsole.log(`Coverage: ${coveredCount}/${total} (${coveragePercent}%)`)\n\t\tconsole.log(\"\")\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\tconsole.log(`Missing screen.meta.ts (${missingCount} files):`)\n\t\t\tconsole.log(\"\")\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\tconsole.log(` ✗ ${file}`)\n\t\t\t\tconsole.log(` → ${suggestedMetaPath}`)\n\t\t\t}\n\n\t\t\tconsole.log(\"\")\n\t\t}\n\n\t\tif (!passedCoverage) {\n\t\t\tconsole.log(\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\tconsole.log(\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\tconsole.log(\n\t\t\t\t\t` 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\tconsole.log(\"✓ All routes have screen.meta.ts files\")\n\t\t}\n\n\t\t// Check for orphan screens (unreachable screens)\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\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\tconsole.log(\"\")\n\t\t\t\t\tconsole.log(`⚠ Orphan screens detected (${orphans.length}):`)\n\t\t\t\t\tconsole.log(\"\")\n\t\t\t\t\tconsole.log(\" These screens have no entryPoints and are not\")\n\t\t\t\t\tconsole.log(\" referenced in any other screen's 'next' array.\")\n\t\t\t\t\tconsole.log(\"\")\n\t\t\t\t\tfor (const orphan of orphans) {\n\t\t\t\t\t\tconsole.log(` ⚠ ${orphan.id} ${orphan.route}`)\n\t\t\t\t\t}\n\t\t\t\t\tconsole.log(\"\")\n\t\t\t\t\tconsole.log(\" Consider adding entryPoints or removing these screens.\")\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Ignore errors reading screens.json\n\t\t\t}\n\t\t}\n\n\t\tif (hasWarnings) {\n\t\t\tconsole.log(\"\")\n\t\t\tconsole.log(\"Lint completed with warnings.\")\n\t\t}\n\t},\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 { execSync } from \"node:child_process\"\nimport { existsSync, readFileSync } from \"node:fs\"\nimport { basename, dirname, join } from \"node:path\"\nimport type { Screen } from \"@screenbook/core\"\nimport { define } from \"gunshi\"\nimport { loadConfig } from \"../utils/config.js\"\nimport { analyzeImpact, type ImpactResult } from \"../utils/impactAnalysis.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\tconsole.error(\"Error: Failed to get changed files from git\")\n\t\t\tconsole.error(\"\")\n\t\t\tconsole.error(\n\t\t\t\t\"Make sure you are in a git repository and the base branch exists.\",\n\t\t\t)\n\t\t\tconsole.error(`Base branch: ${baseBranch}`)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tif (changedFiles.length === 0) {\n\t\t\tconsole.log(\"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\tconsole.log(\"## Screenbook Impact Analysis\")\n\t\t\t\tconsole.log(\"\")\n\t\t\t\tconsole.log(\"No API-related changes detected in this PR.\")\n\t\t\t\tconsole.log(\"\")\n\t\t\t\tconsole.log(`Changed files: ${changedFiles.length}`)\n\t\t\t} else {\n\t\t\t\tconsole.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\tconsole.error(\"Error: screens.json not found\")\n\t\t\tconsole.error(\"\")\n\t\t\tconsole.error(\n\t\t\t\t\"Run 'screenbook build' first to generate the screen catalog.\",\n\t\t\t)\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\tconsole.error(\"Error: Failed to read screens.json\")\n\t\t\tconsole.error(error instanceof Error ? error.message : String(error))\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\tconsole.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\tconsole.log(formatMarkdown(changedFiles, apiNames, results))\n\t\t}\n\t},\n})\n\n/**\n * Extract potential API names from changed file paths.\n * Looks for common API file patterns.\n */\nfunction 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\nfunction 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 */\nfunction 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 { screen, 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","#!/usr/bin/env node\n\nimport { cli, define } from \"gunshi\"\nimport { buildCommand } from \"./commands/build.js\"\nimport { devCommand } from \"./commands/dev.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(\"\")\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},\n})\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@screenbook/cli",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "CLI for Screenbook - screen catalog and navigation graph generator",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"screenbook": "./dist/index.mjs"
|
|
8
|
+
},
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"types": "./dist/index.d.mts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"gunshi": "^0.27.0",
|
|
20
|
+
"jiti": "^2.6.1",
|
|
21
|
+
"minimatch": "^10.1.1",
|
|
22
|
+
"prompts": "^2.4.2",
|
|
23
|
+
"tinyglobby": "^0.2.0",
|
|
24
|
+
"@screenbook/core": "0.0.1"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/prompts": "^2.4.9",
|
|
28
|
+
"tsdown": "^0.18.0"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"screenbook",
|
|
32
|
+
"cli",
|
|
33
|
+
"screen",
|
|
34
|
+
"catalog",
|
|
35
|
+
"navigation"
|
|
36
|
+
],
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "https://github.com/wadakatu/screenbook.git",
|
|
41
|
+
"directory": "packages/cli"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://github.com/wadakatu/screenbook#readme",
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/wadakatu/screenbook/issues"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsdown",
|
|
49
|
+
"dev": "tsdown --watch",
|
|
50
|
+
"test": "vitest run",
|
|
51
|
+
"test:watch": "vitest"
|
|
52
|
+
}
|
|
53
|
+
}
|