@viberails/scanner 0.2.3 → 0.3.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/dist/index.cjs CHANGED
@@ -78,6 +78,7 @@ function aggregateStacks(packages) {
78
78
  }
79
79
  const styling = packages.find((p) => p.stack.styling)?.stack.styling;
80
80
  const backend = packages.find((p) => p.stack.backend)?.stack.backend;
81
+ const orm = packages.find((p) => p.stack.orm)?.stack.orm;
81
82
  const linter = packages.find((p) => p.stack.linter)?.stack.linter;
82
83
  const formatter = packages.find((p) => p.stack.formatter)?.stack.formatter;
83
84
  const testRunner = packages.find((p) => p.stack.testRunner)?.stack.testRunner;
@@ -88,6 +89,7 @@ function aggregateStacks(packages) {
88
89
  libraries: [...libraryMap.values()],
89
90
  styling,
90
91
  backend,
92
+ orm,
91
93
  linter,
92
94
  formatter,
93
95
  testRunner
@@ -514,44 +516,28 @@ async function detectImportAlias(projectPath) {
514
516
  var import_promises5 = require("fs/promises");
515
517
  var import_node_path5 = require("path");
516
518
 
517
- // src/utils/read-package-json.ts
518
- var import_promises4 = require("fs/promises");
519
- var import_node_path4 = require("path");
520
- async function readPackageJson(projectPath) {
521
- try {
522
- const raw = await (0, import_promises4.readFile)((0, import_node_path4.join)(projectPath, "package.json"), "utf-8");
523
- return JSON.parse(raw);
524
- } catch {
525
- return null;
526
- }
527
- }
528
-
529
- // src/detect-stack.ts
530
- function extractMajorVersion(range) {
531
- const match = range.match(/(\d+)/);
532
- return match?.[1];
533
- }
534
- async function fileExists(filePath) {
535
- try {
536
- await (0, import_promises5.access)(filePath);
537
- return true;
538
- } catch {
539
- return false;
540
- }
541
- }
519
+ // src/stack-mappings.ts
542
520
  var FRAMEWORK_MAPPINGS = [
543
521
  { dep: "next", name: "nextjs" },
544
522
  { dep: "expo", name: "expo" },
545
- { dep: "react-native", name: "react-native", excludeDep: "expo" },
523
+ { dep: "react-native", name: "react-native", excludeDeps: ["expo"] },
546
524
  { dep: "@angular/core", name: "angular" },
547
525
  { dep: "@sveltejs/kit", name: "sveltekit" },
548
- { dep: "svelte", name: "svelte" },
526
+ { dep: "svelte", name: "svelte", excludeDeps: ["@sveltejs/kit", "astro"] },
549
527
  { dep: "astro", name: "astro" },
550
528
  { dep: "@remix-run/react", name: "remix" },
551
529
  { dep: "nuxt", name: "nuxt" },
552
- { dep: "vue", name: "vue", excludeDep: "nuxt" },
530
+ { dep: "vue", name: "vue", excludeDeps: ["nuxt"] },
553
531
  { dep: "gatsby", name: "gatsby" },
554
- { dep: "react", name: "react", excludeDep: "next" }
532
+ { dep: "solid-js", name: "solidjs" },
533
+ { dep: "@builder.io/qwik", name: "qwik" },
534
+ { dep: "electron", name: "electron" },
535
+ { dep: "@tauri-apps/api", name: "tauri" },
536
+ {
537
+ dep: "react",
538
+ name: "react",
539
+ excludeDeps: ["next", "@remix-run/react", "gatsby", "expo"]
540
+ }
555
541
  ];
556
542
  var BACKEND_MAPPINGS = [
557
543
  { dep: "@nestjs/core", name: "nestjs" },
@@ -561,20 +547,78 @@ var BACKEND_MAPPINGS = [
561
547
  { dep: "hono", name: "hono" },
562
548
  { dep: "@supabase/supabase-js", name: "supabase" },
563
549
  { dep: "firebase", name: "firebase" },
550
+ { dep: "convex", name: "convex" }
551
+ ];
552
+ var ORM_MAPPINGS = [
564
553
  { dep: "@prisma/client", name: "prisma" },
565
554
  { dep: "prisma", name: "prisma" },
566
- { dep: "drizzle-orm", name: "drizzle" }
555
+ { dep: "drizzle-orm", name: "drizzle" },
556
+ { dep: "typeorm", name: "typeorm" },
557
+ { dep: "sequelize", name: "sequelize" },
558
+ { dep: "mongoose", name: "mongoose" },
559
+ { dep: "kysely", name: "kysely" },
560
+ { dep: "@mikro-orm/core", name: "mikro-orm" }
567
561
  ];
568
562
  var STYLING_MAPPINGS = [
569
563
  { dep: "tailwindcss", name: "tailwindcss" },
570
564
  { dep: "styled-components", name: "styled-components" },
571
565
  { dep: "@emotion/react", name: "emotion" },
572
- { dep: "sass", name: "sass" }
566
+ { dep: "sass", name: "sass" },
567
+ { dep: "@vanilla-extract/css", name: "vanilla-extract" },
568
+ { dep: "unocss", name: "unocss" },
569
+ { dep: "@pandacss/dev", name: "panda-css" },
570
+ { dep: "nativewind", name: "nativewind" }
573
571
  ];
574
572
  var LIBRARY_MAPPINGS = [
573
+ // Validation
575
574
  { deps: ["zod"], name: "zod" },
576
- { deps: ["@trpc/server", "@trpc/client"], name: "trpc" },
577
- { deps: ["@tanstack/react-query"], name: "react-query" }
575
+ // API
576
+ { deps: ["@trpc/server"], name: "trpc" },
577
+ { deps: ["@tanstack/react-query"], name: "react-query" },
578
+ { deps: ["@apollo/client"], name: "apollo" },
579
+ { deps: ["urql"], name: "urql" },
580
+ { deps: ["graphql"], name: "graphql" },
581
+ // State management
582
+ { deps: ["@reduxjs/toolkit"], name: "redux-toolkit" },
583
+ { deps: ["zustand"], name: "zustand" },
584
+ { deps: ["jotai"], name: "jotai" },
585
+ { deps: ["recoil"], name: "recoil" },
586
+ { deps: ["mobx"], name: "mobx" },
587
+ { deps: ["xstate"], name: "xstate" },
588
+ { deps: ["valtio"], name: "valtio" },
589
+ // Forms
590
+ { deps: ["react-hook-form"], name: "react-hook-form" },
591
+ { deps: ["formik"], name: "formik" },
592
+ // HTTP
593
+ { deps: ["axios"], name: "axios" },
594
+ // Auth
595
+ { deps: ["next-auth"], name: "next-auth" },
596
+ { deps: ["@auth/core"], name: "auth-js" },
597
+ { deps: ["@clerk/nextjs"], name: "clerk" },
598
+ { deps: ["lucia"], name: "lucia" },
599
+ // Dates
600
+ { deps: ["date-fns"], name: "date-fns" },
601
+ { deps: ["dayjs"], name: "dayjs" },
602
+ { deps: ["luxon"], name: "luxon" },
603
+ // i18n
604
+ { deps: ["i18next"], name: "i18next" },
605
+ { deps: ["next-i18next"], name: "next-i18next" },
606
+ // Payments
607
+ { deps: ["stripe"], name: "stripe" },
608
+ // Realtime
609
+ { deps: ["socket.io"], name: "socket.io" },
610
+ // Testing utilities
611
+ { deps: ["@testing-library/react"], name: "testing-library" },
612
+ { deps: ["msw"], name: "msw" },
613
+ { deps: ["storybook", "@storybook/react"], name: "storybook" },
614
+ // Bundlers
615
+ { deps: ["vite"], name: "vite" },
616
+ { deps: ["webpack"], name: "webpack" },
617
+ { deps: ["esbuild"], name: "esbuild" },
618
+ { deps: ["@rspack/core"], name: "rspack" },
619
+ // Monorepo
620
+ { deps: ["nx"], name: "nx" },
621
+ { deps: ["lerna"], name: "lerna" }
578
622
  ];
579
623
  var LOCK_FILE_MAP = [
580
624
  { file: "pnpm-lock.yaml", name: "pnpm" },
@@ -582,6 +626,32 @@ var LOCK_FILE_MAP = [
582
626
  { file: "bun.lockb", name: "bun" },
583
627
  { file: "package-lock.json", name: "npm" }
584
628
  ];
629
+
630
+ // src/utils/read-package-json.ts
631
+ var import_promises4 = require("fs/promises");
632
+ var import_node_path4 = require("path");
633
+ async function readPackageJson(projectPath) {
634
+ try {
635
+ const raw = await (0, import_promises4.readFile)((0, import_node_path4.join)(projectPath, "package.json"), "utf-8");
636
+ return JSON.parse(raw);
637
+ } catch {
638
+ return null;
639
+ }
640
+ }
641
+
642
+ // src/detect-stack.ts
643
+ function extractMajorVersion(range) {
644
+ const match = range.match(/(\d+)/);
645
+ return match?.[1];
646
+ }
647
+ async function fileExists(filePath) {
648
+ try {
649
+ await (0, import_promises5.access)(filePath);
650
+ return true;
651
+ } catch {
652
+ return false;
653
+ }
654
+ }
585
655
  async function detectStack(projectPath, additionalDeps) {
586
656
  const pkg = await readPackageJson(projectPath);
587
657
  const allDeps = {
@@ -593,6 +663,7 @@ async function detectStack(projectPath, additionalDeps) {
593
663
  const language = await detectLanguage(projectPath, allDeps);
594
664
  const styling = detectFirst(allDeps, STYLING_MAPPINGS);
595
665
  const backend = detectFirst(allDeps, BACKEND_MAPPINGS);
666
+ const orm = detectFirst(allDeps, ORM_MAPPINGS);
596
667
  const packageManager = await detectPackageManager(projectPath);
597
668
  const linter = detectLinter(allDeps);
598
669
  const formatter = detectFormatter(allDeps);
@@ -603,6 +674,7 @@ async function detectStack(projectPath, additionalDeps) {
603
674
  language,
604
675
  ...styling && { styling },
605
676
  ...backend && { backend },
677
+ ...orm && { orm },
606
678
  packageManager,
607
679
  ...linter && { linter },
608
680
  ...formatter && { formatter },
@@ -613,7 +685,7 @@ async function detectStack(projectPath, additionalDeps) {
613
685
  function detectFramework(allDeps) {
614
686
  for (const mapping of FRAMEWORK_MAPPINGS) {
615
687
  if (!(mapping.dep in allDeps)) continue;
616
- if (mapping.excludeDep && mapping.excludeDep in allDeps) continue;
688
+ if (mapping.excludeDeps?.some((dep) => dep in allDeps)) continue;
617
689
  return {
618
690
  name: mapping.name,
619
691
  version: extractMajorVersion(allDeps[mapping.dep])
@@ -991,7 +1063,7 @@ async function scan(projectPath, _options) {
991
1063
  }
992
1064
 
993
1065
  // src/index.ts
994
- var VERSION = "0.2.3";
1066
+ var VERSION = "0.3.1";
995
1067
  // Annotate the CommonJS export names for ESM import in node:
996
1068
  0 && (module.exports = {
997
1069
  VERSION,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/aggregate.ts","../src/compute-statistics.ts","../src/utils/walk-directory.ts","../src/detect-conventions.ts","../src/utils/classify-filename.ts","../src/detect-stack.ts","../src/utils/read-package-json.ts","../src/detect-structure.ts","../src/utils/classify-directory.ts","../src/detect-workspace.ts","../src/scan.ts","../src/scan-package.ts"],"sourcesContent":["declare const __PACKAGE_VERSION__: string;\nexport const VERSION: string = __PACKAGE_VERSION__;\n\nexport {\n aggregateConventions,\n aggregateStacks,\n aggregateStatistics,\n aggregateStructures,\n} from './aggregate.js';\nexport { computeStatistics } from './compute-statistics.js';\nexport { detectConventions } from './detect-conventions.js';\nexport { detectStack, extractMajorVersion } from './detect-stack.js';\nexport { detectStructure } from './detect-structure.js';\nexport { detectWorkspace } from './detect-workspace.js';\nexport type { ScanOptions } from './scan.js';\nexport { scan } from './scan.js';\nexport { scanPackage } from './scan-package.js';\nexport type { PackageJson } from './utils/read-package-json.js';\nexport { readPackageJson } from './utils/read-package-json.js';\nexport type { WalkedDirectory } from './utils/walk-directory.js';\nexport { walkDirectory } from './utils/walk-directory.js';\n","import {\n type CodebaseStatistics,\n confidenceFromConsistency,\n type DetectedConvention,\n type DetectedStack,\n type DetectedStructure,\n type PackageScanResult,\n} from '@viberails/types';\n\n/** Framework priority order — higher-priority frameworks become the primary. */\nconst FRAMEWORK_PRIORITY = [\n 'nextjs',\n 'sveltekit',\n 'astro',\n 'expo',\n 'react-native',\n 'svelte',\n 'vue',\n 'react',\n];\n\n/**\n * Combines per-package stacks into a single aggregate stack.\n *\n * TypeScript wins over JavaScript if any package uses it.\n * The highest-priority framework becomes the primary; others go into libraries.\n */\nexport function aggregateStacks(packages: PackageScanResult[]): DetectedStack {\n if (packages.length === 1) return packages[0].stack;\n\n const tsPackage = packages.find((p) => p.stack.language.name === 'typescript');\n const language = tsPackage ? tsPackage.stack.language : packages[0].stack.language;\n\n const packageManager = packages[0].stack.packageManager;\n\n const frameworkPackages = packages.filter((p) => p.stack.framework);\n let framework: (typeof packages)[0]['stack']['framework'];\n if (frameworkPackages.length > 0) {\n frameworkPackages.sort((a, b) => {\n const aIdx = FRAMEWORK_PRIORITY.indexOf(a.stack.framework?.name ?? '');\n const bIdx = FRAMEWORK_PRIORITY.indexOf(b.stack.framework?.name ?? '');\n return (aIdx === -1 ? Infinity : aIdx) - (bIdx === -1 ? Infinity : bIdx);\n });\n framework = frameworkPackages[0].stack.framework;\n }\n\n const libraryMap = new Map<string, { name: string; version?: string }>();\n\n for (const pkg of packages) {\n if (pkg.stack.framework && pkg.stack.framework.name !== framework?.name) {\n libraryMap.set(pkg.stack.framework.name, pkg.stack.framework);\n }\n for (const lib of pkg.stack.libraries) {\n if (!libraryMap.has(lib.name)) {\n libraryMap.set(lib.name, lib);\n }\n }\n }\n\n const styling = packages.find((p) => p.stack.styling)?.stack.styling;\n const backend = packages.find((p) => p.stack.backend)?.stack.backend;\n const linter = packages.find((p) => p.stack.linter)?.stack.linter;\n const formatter = packages.find((p) => p.stack.formatter)?.stack.formatter;\n const testRunner = packages.find((p) => p.stack.testRunner)?.stack.testRunner;\n\n return {\n language,\n packageManager,\n framework,\n libraries: [...libraryMap.values()],\n styling,\n backend,\n linter,\n formatter,\n testRunner,\n };\n}\n\n/**\n * Combines per-package structures into a single aggregate structure.\n *\n * Directory paths are prefixed with the package's relativePath.\n */\nexport function aggregateStructures(packages: PackageScanResult[]): DetectedStructure {\n if (packages.length === 1) return packages[0].structure;\n\n const srcDir = packages.some((p) => p.structure.srcDir) ? 'src' : undefined;\n\n const directories = packages.flatMap((pkg) =>\n pkg.structure.directories.map((dir) => ({\n ...dir,\n path: pkg.relativePath ? `${pkg.relativePath}/${dir.path}` : dir.path,\n })),\n );\n\n const testPatterns = packages\n .map((p) => p.structure.testPattern)\n .filter((t): t is DetectedConvention<string> => t !== undefined);\n\n let testPattern: DetectedConvention<string> | undefined;\n if (testPatterns.length > 0) {\n const counts = new Map<string, { count: number; pattern: DetectedConvention<string> }>();\n for (const tp of testPatterns) {\n const existing = counts.get(tp.value);\n if (existing) {\n existing.count++;\n } else {\n counts.set(tp.value, { count: 1, pattern: tp });\n }\n }\n let best = { count: 0, pattern: testPatterns[0] };\n for (const entry of counts.values()) {\n if (entry.count > best.count) best = entry;\n }\n testPattern = best.pattern;\n }\n\n return { srcDir, directories, testPattern };\n}\n\n/**\n * Combines per-package conventions into aggregate conventions.\n *\n * When all packages agree on a convention, reports it with averaged consistency.\n * When packages disagree, scales consistency by agreement ratio.\n * Omits conventions present in fewer than half of packages.\n */\nexport function aggregateConventions(\n packages: PackageScanResult[],\n): Record<string, DetectedConvention> {\n if (packages.length === 1) return packages[0].conventions;\n\n const allKeys = new Set<string>();\n for (const pkg of packages) {\n for (const key of Object.keys(pkg.conventions)) {\n allKeys.add(key);\n }\n }\n\n const result: Record<string, DetectedConvention> = {};\n\n for (const key of allKeys) {\n const entries = packages\n .map((p) => p.conventions[key])\n .filter((c): c is DetectedConvention => c !== undefined);\n\n if (entries.length < packages.length / 2) continue;\n\n const valueCounts = new Map<\n string,\n { count: number; totalConsistency: number; totalSamples: number }\n >();\n for (const entry of entries) {\n const existing = valueCounts.get(entry.value);\n if (existing) {\n existing.count++;\n existing.totalConsistency += entry.consistency;\n existing.totalSamples += entry.sampleSize;\n } else {\n valueCounts.set(entry.value, {\n count: 1,\n totalConsistency: entry.consistency,\n totalSamples: entry.sampleSize,\n });\n }\n }\n\n let majorityValue = '';\n let majorityData = { count: 0, totalConsistency: 0, totalSamples: 0 };\n for (const [value, data] of valueCounts) {\n if (data.count > majorityData.count) {\n majorityValue = value;\n majorityData = data;\n }\n }\n\n const agreement = majorityData.count / entries.length;\n const avgConsistency = majorityData.totalConsistency / majorityData.count;\n const consistency = Math.round(avgConsistency * agreement);\n\n result[key] = {\n value: majorityValue,\n confidence: confidenceFromConsistency(consistency),\n sampleSize: majorityData.totalSamples,\n consistency,\n };\n }\n\n return result;\n}\n\n/**\n * Combines per-package statistics into aggregate statistics.\n *\n * Sums totals, recomputes averages, merges largest files with path prefixing.\n */\nexport function aggregateStatistics(packages: PackageScanResult[]): CodebaseStatistics {\n if (packages.length === 1) return packages[0].statistics;\n\n const totalFiles = packages.reduce((sum, p) => sum + p.statistics.totalFiles, 0);\n const totalLines = packages.reduce((sum, p) => sum + p.statistics.totalLines, 0);\n const averageFileLines = totalFiles > 0 ? Math.round(totalLines / totalFiles) : 0;\n\n const largestFiles = packages\n .flatMap((pkg) =>\n pkg.statistics.largestFiles.map((f) => ({\n path: pkg.relativePath ? `${pkg.relativePath}/${f.path}` : f.path,\n lines: f.lines,\n })),\n )\n .sort((a, b) => b.lines - a.lines)\n .slice(0, 5);\n\n const filesByExtension: Record<string, number> = {};\n for (const pkg of packages) {\n for (const [ext, count] of Object.entries(pkg.statistics.filesByExtension)) {\n filesByExtension[ext] = (filesByExtension[ext] ?? 0) + count;\n }\n }\n\n return { totalFiles, totalLines, averageFileLines, largestFiles, filesByExtension };\n}\n","import { readdir, readFile } from 'node:fs/promises';\nimport { extname, join } from 'node:path';\nimport type { CodebaseStatistics, FileStatistic } from '@viberails/types';\nimport type { WalkedDirectory } from './utils/walk-directory.js';\nimport { SOURCE_EXTENSIONS, walkDirectory } from './utils/walk-directory.js';\n\n/**\n * Counts lines in a file by reading its contents.\n *\n * @param filePath - Absolute path to the file.\n * @returns Number of lines, or 0 if the file can't be read.\n */\nasync function countLines(filePath: string): Promise<number> {\n try {\n const content = await readFile(filePath, 'utf-8');\n if (content.length === 0) return 0;\n let count = 0;\n for (let i = 0; i < content.length; i++) {\n if (content.charCodeAt(i) === 10) count++;\n }\n // A file with no trailing newline has one more line than newline count\n if (content.charCodeAt(content.length - 1) !== 10) count++;\n return count;\n } catch {\n return 0;\n }\n}\n\n/**\n * Collects root-level source file names from the project directory.\n *\n * @param projectPath - Absolute path to the project root.\n * @returns Array of source file names in the root directory.\n */\nasync function getRootSourceFiles(projectPath: string): Promise<string[]> {\n try {\n const entries = await readdir(projectPath, { withFileTypes: true });\n const sourceFiles: string[] = [];\n for (const entry of entries) {\n if (entry.isFile()) {\n const ext = extname(entry.name);\n if (SOURCE_EXTENSIONS.has(ext)) {\n sourceFiles.push(entry.name);\n }\n }\n }\n return sourceFiles;\n } catch {\n return [];\n }\n}\n\n/**\n * Computes quantitative statistics about a project's source files.\n *\n * Reads each source file and produces aggregate metrics including\n * file counts, line counts, and extension breakdown.\n *\n * @param projectPath - Absolute path to the project root.\n * @param dirs - Pre-walked directory list. If not provided, walks the directory tree.\n * @returns Statistics about the codebase.\n */\nexport async function computeStatistics(\n projectPath: string,\n dirs?: WalkedDirectory[],\n): Promise<CodebaseStatistics> {\n const directories = dirs ?? (await walkDirectory(projectPath));\n const rootFiles = await getRootSourceFiles(projectPath);\n\n // Collect all file paths and extensions\n const filesToProcess: Array<{ relativePath: string; absolutePath: string; ext: string }> = [];\n\n // Root-level source files\n for (const name of rootFiles) {\n filesToProcess.push({\n relativePath: name,\n absolutePath: join(projectPath, name),\n ext: extname(name),\n });\n }\n\n // Files from subdirectories\n for (const dir of directories) {\n for (const name of dir.sourceFileNames) {\n filesToProcess.push({\n relativePath: `${dir.relativePath}/${name}`,\n absolutePath: join(dir.absolutePath, name),\n ext: extname(name),\n });\n }\n }\n\n const totalFiles = filesToProcess.length;\n\n if (totalFiles === 0) {\n return {\n totalFiles: 0,\n totalLines: 0,\n averageFileLines: 0,\n largestFiles: [],\n filesByExtension: {},\n };\n }\n\n // Count lines for all files concurrently\n const lineResults = await Promise.all(\n filesToProcess.map(async (file) => ({\n path: file.relativePath,\n lines: await countLines(file.absolutePath),\n ext: file.ext,\n })),\n );\n\n let totalLines = 0;\n const filesByExtension: Record<string, number> = {};\n const allFiles: FileStatistic[] = [];\n\n for (const result of lineResults) {\n totalLines += result.lines;\n filesByExtension[result.ext] = (filesByExtension[result.ext] ?? 0) + 1;\n allFiles.push({ path: result.path, lines: result.lines });\n }\n\n // Sort descending by lines, take top 5\n allFiles.sort((a, b) => b.lines - a.lines);\n const largestFiles = allFiles.slice(0, 5);\n\n return {\n totalFiles,\n totalLines,\n averageFileLines: Math.round(totalLines / totalFiles),\n largestFiles,\n filesByExtension,\n };\n}\n","import { readdir } from 'node:fs/promises';\nimport { join, relative } from 'node:path';\n\n/** Directories to always skip during scanning. */\nconst IGNORED_DIRS = new Set([\n 'node_modules',\n '.git',\n 'dist',\n 'build',\n '.next',\n '.nuxt',\n '.viberails',\n 'coverage',\n '.turbo',\n '.cache',\n '.output',\n '.expo',\n 'android',\n 'ios',\n 'Pods',\n '.gradle',\n]);\n\n/** Source file extensions to count. */\nexport const SOURCE_EXTENSIONS = new Set([\n '.ts',\n '.tsx',\n '.js',\n '.jsx',\n '.mjs',\n '.cjs',\n '.vue',\n '.svelte',\n '.astro',\n]);\n\nexport interface WalkedDirectory {\n /** Path relative to project root, using forward slashes. */\n relativePath: string;\n /** Absolute path. */\n absolutePath: string;\n /** Number of source files directly in this directory. */\n sourceFileCount: number;\n /** Names of source files in this directory. */\n sourceFileNames: string[];\n /** Depth relative to the project root (1 = direct children). */\n depth: number;\n}\n\n/**\n * Walks a project directory tree using BFS, collecting directory info.\n *\n * @param projectPath - Absolute path to the project root.\n * @param maxDepth - Maximum directory depth to traverse (default 4).\n * @returns Flat list of all visited directories (excluding root itself).\n */\nexport async function walkDirectory(\n projectPath: string,\n maxDepth: number = 4,\n): Promise<WalkedDirectory[]> {\n const results: WalkedDirectory[] = [];\n const queue: Array<{ absolutePath: string; depth: number }> = [];\n\n try {\n const rootEntries = await readdir(projectPath, { withFileTypes: true });\n for (const entry of rootEntries) {\n if (entry.isDirectory() && !entry.isSymbolicLink() && !IGNORED_DIRS.has(entry.name)) {\n queue.push({ absolutePath: join(projectPath, entry.name), depth: 1 });\n }\n }\n } catch {\n return results;\n }\n\n while (queue.length > 0) {\n const item = queue.shift();\n if (!item) break;\n const { absolutePath, depth } = item;\n const sourceFileNames: string[] = [];\n\n try {\n const entries = await readdir(absolutePath, { withFileTypes: true });\n\n for (const entry of entries) {\n if (entry.isSymbolicLink()) continue;\n\n if (entry.isDirectory() && depth < maxDepth && !IGNORED_DIRS.has(entry.name)) {\n queue.push({ absolutePath: join(absolutePath, entry.name), depth: depth + 1 });\n } else if (entry.isFile()) {\n const dotIndex = entry.name.lastIndexOf('.');\n if (dotIndex > 0) {\n const ext = entry.name.substring(dotIndex);\n if (SOURCE_EXTENSIONS.has(ext)) {\n sourceFileNames.push(entry.name);\n }\n }\n }\n }\n } catch {\n continue;\n }\n\n const rel = relative(projectPath, absolutePath).split('\\\\').join('/');\n results.push({\n relativePath: rel,\n absolutePath,\n sourceFileCount: sourceFileNames.length,\n sourceFileNames,\n depth,\n });\n }\n\n return results;\n}\n","import { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { DetectedConvention, DetectedStructure } from '@viberails/types';\nimport { confidenceFromConsistency } from '@viberails/types';\nimport { classifyFilename } from './utils/classify-filename.js';\nimport type { WalkedDirectory } from './utils/walk-directory.js';\nimport { walkDirectory } from './utils/walk-directory.js';\n\n/**\n * Detects coding conventions used in a project by analyzing file names\n * and configuration files.\n *\n * @param projectPath - Absolute path to the project root directory.\n * @param structure - Previously detected directory structure, used to identify\n * directories by role.\n * @param dirs - Pre-walked directory list. If not provided, walks the directory tree.\n * @returns A record of detected conventions keyed by convention name.\n * Only statistical conventions with sampleSize >= 3 are included.\n */\nexport async function detectConventions(\n projectPath: string,\n structure: DetectedStructure,\n dirs?: WalkedDirectory[],\n): Promise<Record<string, DetectedConvention>> {\n if (!dirs) {\n dirs = await walkDirectory(projectPath, 4);\n }\n\n const result: Record<string, DetectedConvention> = {};\n\n const fileNaming = detectFileNaming(dirs);\n if (fileNaming) result.fileNaming = fileNaming;\n\n const componentNaming = detectComponentNaming(dirs, structure);\n if (componentNaming) result.componentNaming = componentNaming;\n\n const hookNaming = detectHookNaming(dirs, structure);\n if (hookNaming) result.hookNaming = hookNaming;\n\n // importAlias is binary (present or not) — bypasses sampleSize threshold\n const importAlias = await detectImportAlias(projectPath);\n if (importAlias) result.importAlias = importAlias;\n\n return result;\n}\n\n/** Strips the file extension, handling multi-dot names like foo.test.ts. */\nfunction stripExtension(filename: string): string {\n const firstDot = filename.indexOf('.');\n return firstDot > 0 ? filename.substring(0, firstDot) : filename;\n}\n\n/**\n * Detects the dominant file naming convention across all directories\n * with 3+ source files. Only returns conventions with consistency >= 70%\n * (medium or high confidence).\n */\nfunction detectFileNaming(dirs: WalkedDirectory[]): DetectedConvention | undefined {\n const conventionCounts = new Map<string, number>();\n let total = 0;\n\n for (const dir of dirs) {\n if (dir.sourceFileCount < 3) continue;\n\n for (const filename of dir.sourceFileNames) {\n if (filename.includes('.test.') || filename.includes('.spec.')) continue;\n const bare = stripExtension(filename);\n if (bare === 'index') continue;\n\n const convention = classifyFilename(bare);\n total++;\n if (convention !== 'unknown') {\n conventionCounts.set(convention, (conventionCounts.get(convention) ?? 0) + 1);\n }\n }\n }\n\n if (total < 3) return undefined;\n\n const sorted = [...conventionCounts.entries()].sort((a, b) => b[1] - a[1]);\n if (sorted.length === 0) return undefined;\n\n const [dominantConvention, dominantCount] = sorted[0];\n const consistency = Math.round((dominantCount / total) * 100);\n const confidence = confidenceFromConsistency(consistency);\n\n if (confidence === 'low') return undefined;\n\n return {\n value: dominantConvention,\n confidence,\n sampleSize: total,\n consistency,\n };\n}\n\n/**\n * Detects component naming convention by checking if .tsx files in\n * component directories use PascalCase filenames.\n */\nfunction detectComponentNaming(\n dirs: WalkedDirectory[],\n structure: DetectedStructure,\n): DetectedConvention | undefined {\n const componentPaths = new Set(\n structure.directories.filter((d) => d.role === 'components').map((d) => d.path),\n );\n\n const tsxFiles: string[] = [];\n for (const dir of dirs) {\n if (!componentPaths.has(dir.relativePath)) continue;\n for (const f of dir.sourceFileNames) {\n if (f.endsWith('.tsx')) tsxFiles.push(f);\n }\n }\n\n if (tsxFiles.length < 3) return undefined;\n\n const pascalCount = tsxFiles.filter((f) => /^[A-Z]/.test(stripExtension(f))).length;\n const consistency = Math.round((pascalCount / tsxFiles.length) * 100);\n const dominantValue = pascalCount >= tsxFiles.length / 2 ? 'PascalCase' : 'camelCase';\n\n return {\n value: dominantValue,\n confidence: confidenceFromConsistency(consistency),\n sampleSize: tsxFiles.length,\n consistency,\n };\n}\n\n/**\n * Detects hook naming convention by checking if hook files use\n * kebab-case (use-*) or camelCase (useXxx) prefix.\n */\nfunction detectHookNaming(\n dirs: WalkedDirectory[],\n structure: DetectedStructure,\n): DetectedConvention | undefined {\n const hookPaths = new Set(\n structure.directories.filter((d) => d.role === 'hooks').map((d) => d.path),\n );\n\n const hookFiles: string[] = [];\n for (const dir of dirs) {\n if (!hookPaths.has(dir.relativePath)) continue;\n for (const f of dir.sourceFileNames) {\n const bare = stripExtension(f);\n if (bare.startsWith('use-') || /^use[A-Z]/.test(bare)) {\n hookFiles.push(f);\n }\n }\n }\n\n if (hookFiles.length < 3) return undefined;\n\n let kebabCount = 0;\n let camelCount = 0;\n\n for (const filename of hookFiles) {\n const bare = stripExtension(filename);\n if (bare.startsWith('use-')) {\n kebabCount++;\n } else if (/^use[A-Z]/.test(bare)) {\n camelCount++;\n }\n }\n\n const total = hookFiles.length;\n const isDominantKebab = kebabCount >= camelCount;\n const dominantCount = isDominantKebab ? kebabCount : camelCount;\n const consistency = Math.round((dominantCount / total) * 100);\n\n return {\n value: isDominantKebab ? 'use-*' : 'useXxx',\n confidence: confidenceFromConsistency(consistency),\n sampleSize: total,\n consistency,\n };\n}\n\n/** Minimal shape for the tsconfig subset we read. */\ninterface TsConfigSubset {\n compilerOptions?: {\n paths?: Record<string, string[]>;\n };\n}\n\n/**\n * Detects import alias patterns from tsconfig.json or jsconfig.json paths configuration.\n * Checks tsconfig.json first, then falls back to jsconfig.json for JavaScript projects.\n */\nasync function detectImportAlias(projectPath: string): Promise<DetectedConvention | undefined> {\n for (const configFile of ['tsconfig.json', 'jsconfig.json']) {\n try {\n const raw = await readFile(join(projectPath, configFile), 'utf-8');\n const config = JSON.parse(raw) as TsConfigSubset;\n const paths = config.compilerOptions?.paths;\n if (!paths) continue;\n\n const aliases = Object.keys(paths);\n if (aliases.length === 0) continue;\n\n return {\n value: aliases.join(','),\n confidence: 'high',\n sampleSize: aliases.length,\n consistency: 100,\n };\n } catch {}\n }\n return undefined;\n}\n","/**\n * Naming convention types for filenames.\n */\nexport type FilenameConvention =\n | 'kebab-case'\n | 'camelCase'\n | 'PascalCase'\n | 'snake_case'\n | 'unknown';\n\nconst PATTERNS = [\n { convention: 'PascalCase', regex: /^[A-Z][a-zA-Z0-9]*$/ },\n { convention: 'camelCase', regex: /^[a-z][a-zA-Z0-9]*[A-Z][a-zA-Z0-9]*$/ },\n { convention: 'kebab-case', regex: /^[a-z][a-z0-9]*(-[a-z0-9]+)+$/ },\n { convention: 'snake_case', regex: /^[a-z][a-z0-9]*(_[a-z0-9]+)+$/ },\n] as const;\n\n/**\n * Classifies a filename (without extension) into a naming convention.\n *\n * Single lowercase words like 'utils' return 'unknown' because they are\n * ambiguous — they could be kebab-case, snake_case, or camelCase without\n * a disambiguating structural marker.\n *\n * @param filename - The bare filename with no extension (e.g. 'user-profile').\n * @returns The detected naming convention, or 'unknown' if ambiguous.\n */\nexport function classifyFilename(filename: string): FilenameConvention {\n for (const { convention, regex } of PATTERNS) {\n if (regex.test(filename)) return convention;\n }\n return 'unknown';\n}\n","import { access } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { DetectedStack, StackItem } from '@viberails/types';\nimport { readPackageJson } from './utils/read-package-json.js';\n\n/**\n * Extracts the major version number from a semver range string.\n *\n * @param range - A semver range such as `\"^15.0.3\"`, `\"~2.1.0\"`, or `\"3.x\"`.\n * @returns The major version string (e.g. `\"15\"`), or `undefined` if extraction fails.\n */\nexport function extractMajorVersion(range: string): string | undefined {\n const match = range.match(/(\\d+)/);\n return match?.[1];\n}\n\n/** Check whether a file exists at the given path. */\nasync function fileExists(filePath: string): Promise<boolean> {\n try {\n await access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Detection mapping tables\n// ---------------------------------------------------------------------------\n\ninterface FrameworkMapping {\n /** Package name to look for in dependencies. */\n dep: string;\n /** Name to use in the StackItem. */\n name: string;\n /** If set, the dep must NOT be present for this rule to match. */\n excludeDep?: string;\n}\n\nconst FRAMEWORK_MAPPINGS: FrameworkMapping[] = [\n { dep: 'next', name: 'nextjs' },\n { dep: 'expo', name: 'expo' },\n { dep: 'react-native', name: 'react-native', excludeDep: 'expo' },\n { dep: '@angular/core', name: 'angular' },\n { dep: '@sveltejs/kit', name: 'sveltekit' },\n { dep: 'svelte', name: 'svelte' },\n { dep: 'astro', name: 'astro' },\n { dep: '@remix-run/react', name: 'remix' },\n { dep: 'nuxt', name: 'nuxt' },\n { dep: 'vue', name: 'vue', excludeDep: 'nuxt' },\n { dep: 'gatsby', name: 'gatsby' },\n { dep: 'react', name: 'react', excludeDep: 'next' },\n];\n\nconst BACKEND_MAPPINGS: Array<{ dep: string; name: string }> = [\n { dep: '@nestjs/core', name: 'nestjs' },\n { dep: 'express', name: 'express' },\n { dep: 'fastify', name: 'fastify' },\n { dep: 'koa', name: 'koa' },\n { dep: 'hono', name: 'hono' },\n { dep: '@supabase/supabase-js', name: 'supabase' },\n { dep: 'firebase', name: 'firebase' },\n { dep: '@prisma/client', name: 'prisma' },\n { dep: 'prisma', name: 'prisma' },\n { dep: 'drizzle-orm', name: 'drizzle' },\n];\n\nconst STYLING_MAPPINGS: Array<{ dep: string; name: string }> = [\n { dep: 'tailwindcss', name: 'tailwindcss' },\n { dep: 'styled-components', name: 'styled-components' },\n { dep: '@emotion/react', name: 'emotion' },\n { dep: 'sass', name: 'sass' },\n];\n\nconst LIBRARY_MAPPINGS: Array<{ deps: string[]; name: string }> = [\n { deps: ['zod'], name: 'zod' },\n { deps: ['@trpc/server', '@trpc/client'], name: 'trpc' },\n { deps: ['@tanstack/react-query'], name: 'react-query' },\n];\n\nconst LOCK_FILE_MAP: Array<{ file: string; name: string }> = [\n { file: 'pnpm-lock.yaml', name: 'pnpm' },\n { file: 'yarn.lock', name: 'yarn' },\n { file: 'bun.lockb', name: 'bun' },\n { file: 'package-lock.json', name: 'npm' },\n];\n\n// ---------------------------------------------------------------------------\n// Main detection function\n// ---------------------------------------------------------------------------\n\n/**\n * Detects the technology stack of a project by reading its package.json\n * and checking for lock files and configuration files.\n *\n * When `additionalDeps` is provided, they are merged as a base layer\n * beneath the package's own deps. This allows monorepo root-level deps\n * (e.g. typescript, eslint) to be visible during per-package scanning.\n *\n * @param projectPath - Absolute path to the project root directory.\n * @param additionalDeps - Optional base dependencies merged under package deps.\n * @returns The detected technology stack.\n */\nexport async function detectStack(\n projectPath: string,\n additionalDeps?: Record<string, string>,\n): Promise<DetectedStack> {\n const pkg = await readPackageJson(projectPath);\n const allDeps: Record<string, string> = {\n ...additionalDeps,\n ...pkg?.dependencies,\n ...pkg?.devDependencies,\n };\n\n const framework = detectFramework(allDeps);\n const language = await detectLanguage(projectPath, allDeps);\n const styling = detectFirst(allDeps, STYLING_MAPPINGS);\n const backend = detectFirst(allDeps, BACKEND_MAPPINGS);\n const packageManager = await detectPackageManager(projectPath);\n const linter = detectLinter(allDeps);\n const formatter = detectFormatter(allDeps);\n const testRunner = detectTestRunner(allDeps);\n const libraries = detectLibraries(allDeps);\n\n return {\n ...(framework && { framework }),\n language,\n ...(styling && { styling }),\n ...(backend && { backend }),\n packageManager,\n ...(linter && { linter }),\n ...(formatter && { formatter }),\n ...(testRunner && { testRunner }),\n libraries,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Individual detectors\n// ---------------------------------------------------------------------------\n\nfunction detectFramework(allDeps: Record<string, string>): StackItem | undefined {\n for (const mapping of FRAMEWORK_MAPPINGS) {\n if (!(mapping.dep in allDeps)) continue;\n if (mapping.excludeDep && mapping.excludeDep in allDeps) continue;\n return {\n name: mapping.name,\n version: extractMajorVersion(allDeps[mapping.dep]),\n };\n }\n return undefined;\n}\n\nasync function detectLanguage(\n projectPath: string,\n allDeps: Record<string, string>,\n): Promise<StackItem> {\n if ('typescript' in allDeps) {\n return {\n name: 'typescript',\n version: extractMajorVersion(allDeps.typescript),\n };\n }\n if (await fileExists(join(projectPath, 'tsconfig.json'))) {\n return { name: 'typescript' };\n }\n return { name: 'javascript' };\n}\n\nfunction detectFirst(\n allDeps: Record<string, string>,\n mappings: Array<{ dep: string; name: string }>,\n): StackItem | undefined {\n for (const mapping of mappings) {\n if (mapping.dep in allDeps) {\n return {\n name: mapping.name,\n version: extractMajorVersion(allDeps[mapping.dep]),\n };\n }\n }\n return undefined;\n}\n\nasync function detectPackageManager(projectPath: string): Promise<StackItem> {\n for (const entry of LOCK_FILE_MAP) {\n if (await fileExists(join(projectPath, entry.file))) {\n return { name: entry.name };\n }\n }\n return { name: 'npm' };\n}\n\nfunction detectLinter(allDeps: Record<string, string>): StackItem | undefined {\n if ('eslint' in allDeps) {\n return { name: 'eslint', version: extractMajorVersion(allDeps.eslint) };\n }\n if ('@biomejs/biome' in allDeps) {\n return {\n name: 'biome',\n version: extractMajorVersion(allDeps['@biomejs/biome']),\n };\n }\n return undefined;\n}\n\nfunction detectFormatter(allDeps: Record<string, string>): StackItem | undefined {\n if ('prettier' in allDeps) {\n return { name: 'prettier', version: extractMajorVersion(allDeps.prettier) };\n }\n if ('@biomejs/biome' in allDeps) {\n return {\n name: 'biome',\n version: extractMajorVersion(allDeps['@biomejs/biome']),\n };\n }\n return undefined;\n}\n\nfunction detectTestRunner(allDeps: Record<string, string>): StackItem | undefined {\n if ('vitest' in allDeps) {\n return { name: 'vitest', version: extractMajorVersion(allDeps.vitest) };\n }\n if ('jest' in allDeps) {\n return { name: 'jest', version: extractMajorVersion(allDeps.jest) };\n }\n if ('@playwright/test' in allDeps) {\n return { name: 'playwright', version: extractMajorVersion(allDeps['@playwright/test']) };\n }\n if ('cypress' in allDeps) {\n return { name: 'cypress', version: extractMajorVersion(allDeps.cypress) };\n }\n if ('mocha' in allDeps) {\n return { name: 'mocha', version: extractMajorVersion(allDeps.mocha) };\n }\n return undefined;\n}\n\nfunction detectLibraries(allDeps: Record<string, string>): StackItem[] {\n const libs: StackItem[] = [];\n for (const mapping of LIBRARY_MAPPINGS) {\n const found = mapping.deps.find((dep) => dep in allDeps);\n if (found) {\n libs.push({\n name: mapping.name,\n version: extractMajorVersion(allDeps[found]),\n });\n }\n }\n return libs;\n}\n","import { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\n/**\n * Minimal interface for the fields we read from package.json.\n */\nexport interface PackageJson {\n name?: string;\n version?: string;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n}\n\n/**\n * Safely reads and parses a package.json file from the given directory.\n *\n * @param projectPath - Absolute path to the project root directory.\n * @returns Parsed package.json contents, or `null` if the file doesn't exist or is invalid JSON.\n */\nexport async function readPackageJson(projectPath: string): Promise<PackageJson | null> {\n try {\n const raw = await readFile(join(projectPath, 'package.json'), 'utf-8');\n return JSON.parse(raw) as PackageJson;\n } catch {\n return null;\n }\n}\n","import type { DetectedConvention, DetectedStructure } from '@viberails/types';\nimport { confidenceFromConsistency } from '@viberails/types';\nimport { classifyDirectory } from './utils/classify-directory.js';\nimport type { WalkedDirectory } from './utils/walk-directory.js';\nimport { walkDirectory } from './utils/walk-directory.js';\n\n/**\n * Detects the directory structure and organization of a project.\n *\n * Classifies directories by role, detects whether a src/ directory is in use,\n * and identifies test file patterns.\n *\n * @param projectPath - Absolute path to the project root directory.\n * @param dirs - Pre-walked directory list. If not provided, walks the directory tree.\n * @returns The detected directory structure.\n */\nexport async function detectStructure(\n projectPath: string,\n dirs?: WalkedDirectory[],\n): Promise<DetectedStructure> {\n if (!dirs) {\n dirs = await walkDirectory(projectPath, 4);\n }\n\n // Detect srcDir\n const hasSrcDir = dirs.some((d) => d.relativePath === 'src' || d.relativePath.startsWith('src/'));\n const srcDir = hasSrcDir ? 'src' : undefined;\n\n // Classify directories\n const directories = dirs.map((d) => classifyDirectory(d)).filter((d) => d !== null);\n\n // Detect test pattern\n const testPattern = detectTestPattern(dirs);\n\n return {\n ...(srcDir !== undefined && { srcDir }),\n directories,\n ...(testPattern !== undefined && { testPattern }),\n };\n}\n\n/**\n * Detects the dominant test file naming pattern from all source files.\n * Returns undefined if fewer than 3 test files are found.\n */\nfunction detectTestPattern(\n dirs: Array<{ sourceFileNames: string[] }>,\n): DetectedConvention<string> | undefined {\n const allFiles = dirs.flatMap((d) => d.sourceFileNames);\n const testFiles = allFiles.filter((f) => f.includes('.test.') || f.includes('.spec.'));\n\n if (testFiles.length < 3) return undefined;\n\n const dotTestCount = testFiles.filter((f) => f.includes('.test.')).length;\n const dotSpecCount = testFiles.filter((f) => f.includes('.spec.')).length;\n\n const isDotTest = dotTestCount >= dotSpecCount;\n const dominantSep = isDotTest ? '.test.' : '.spec.';\n const dominantCount = isDotTest ? dotTestCount : dotSpecCount;\n const consistency = Math.round((dominantCount / testFiles.length) * 100);\n\n // Find most common extension among dominant test files\n const extCounts = new Map<string, number>();\n for (const f of testFiles.filter((f) => f.includes(dominantSep))) {\n const ext = f.substring(f.lastIndexOf('.'));\n extCounts.set(ext, (extCounts.get(ext) ?? 0) + 1);\n }\n const topExt = [...extCounts.entries()].sort((a, b) => b[1] - a[1])[0]?.[0] ?? '.ts';\n\n const sep = isDotTest ? 'test' : 'spec';\n\n return {\n value: `*.${sep}${topExt}`,\n confidence: confidenceFromConsistency(consistency),\n sampleSize: testFiles.length,\n consistency,\n };\n}\n","import type { Confidence, DirectoryRole } from '@viberails/types';\nimport type { WalkedDirectory } from './walk-directory.js';\n\nexport interface ClassifiedDirectory {\n path: string;\n role: DirectoryRole;\n fileCount: number;\n confidence: Confidence;\n}\n\ninterface RolePattern {\n role: DirectoryRole;\n pathPatterns: string[];\n}\n\nconst ROLE_PATTERNS: RolePattern[] = [\n { role: 'pages', pathPatterns: ['src/app', 'src/pages', 'app', 'pages'] },\n { role: 'components', pathPatterns: ['src/components', 'components'] },\n { role: 'hooks', pathPatterns: ['src/hooks', 'hooks'] },\n {\n role: 'utils',\n pathPatterns: ['src/lib', 'src/utils', 'src/helpers', 'lib', 'utils', 'helpers'],\n },\n { role: 'types', pathPatterns: ['src/types', 'types', 'src/@types', '@types'] },\n { role: 'tests', pathPatterns: ['__tests__', 'tests', 'test', 'src/__tests__', 'src/tests'] },\n { role: 'styles', pathPatterns: ['src/styles', 'styles', 'src/css', 'css'] },\n { role: 'api', pathPatterns: ['src/api', 'api', 'src/app/api', 'app/api'] },\n { role: 'config', pathPatterns: ['config', 'src/config'] },\n];\n\n/**\n * Classifies a directory by its role in the project.\n *\n * @param dir - A walked directory entry.\n * @returns Classified directory info, or null if the directory should be skipped.\n */\nexport function classifyDirectory(dir: WalkedDirectory): ClassifiedDirectory | null {\n // Try name-based matching first\n const nameMatch = matchByName(dir.relativePath);\n if (nameMatch) {\n const confidence: Confidence = dir.sourceFileCount > 0 ? 'high' : 'low';\n return {\n path: dir.relativePath,\n role: nameMatch,\n fileCount: dir.sourceFileCount,\n confidence,\n };\n }\n\n // Skip directories with no source files if no name match\n if (dir.sourceFileCount === 0) return null;\n\n // Content-based heuristics\n const contentRole = inferFromContent(dir);\n if (contentRole) return contentRole;\n\n // Has source files but no classification\n return {\n path: dir.relativePath,\n role: 'unknown',\n fileCount: dir.sourceFileCount,\n confidence: 'low',\n };\n}\n\n/**\n * Matches a relative path against known role patterns.\n * Supports suffix matching so monorepo paths like `apps/web/lib`\n * match patterns like `lib`.\n * Returns the role if matched, or null.\n */\nfunction matchByName(relativePath: string): DirectoryRole | null {\n for (const { role, pathPatterns } of ROLE_PATTERNS) {\n for (const pattern of pathPatterns) {\n if (relativePath === pattern || relativePath.endsWith(`/${pattern}`)) {\n return role;\n }\n }\n }\n return null;\n}\n\n/**\n * Infers directory role from file contents/names.\n */\nfunction inferFromContent(dir: WalkedDirectory): ClassifiedDirectory | null {\n const { sourceFileNames, sourceFileCount } = dir;\n\n // Check for hook files (use-* kebab or useXxx camelCase prefix)\n // Require at least 2 matching files to avoid misclassifying directories\n // with a single hook utility (e.g. lib/ with one useXxx file)\n const hookFiles = sourceFileNames.filter((f) => {\n const name = f.split('.')[0];\n return name.startsWith('use-') || /^use[A-Z]/.test(name);\n });\n if (hookFiles.length >= 2 && hookFiles.length / sourceFileCount >= 0.5) {\n return {\n path: dir.relativePath,\n role: 'hooks',\n fileCount: sourceFileCount,\n confidence: hookFiles.length / sourceFileCount >= 0.9 ? 'high' : 'medium',\n };\n }\n\n // Check for test files — require at least 2 to avoid false positives\n const testFiles = sourceFileNames.filter((f) => f.includes('.test.') || f.includes('.spec.'));\n if (testFiles.length >= 2 && testFiles.length / sourceFileCount >= 0.5) {\n return {\n path: dir.relativePath,\n role: 'tests',\n fileCount: sourceFileCount,\n confidence: testFiles.length / sourceFileCount >= 0.9 ? 'high' : 'medium',\n };\n }\n\n return null;\n}\n","import { readdir, readFile } from 'node:fs/promises';\nimport { join, relative } from 'node:path';\nimport type { DetectedWorkspace, WorkspacePackage } from '@viberails/types';\nimport { readPackageJson } from './utils/read-package-json.js';\n\n/**\n * Detects workspace configuration for monorepo projects.\n *\n * Checks for `pnpm-workspace.yaml` first, then falls back to\n * `package.json` `workspaces` field. Returns `undefined` for\n * single-package projects.\n *\n * @param projectRoot - Absolute path to the project root.\n * @returns Detected workspace info, or `undefined` if not a monorepo.\n */\nexport async function detectWorkspace(projectRoot: string): Promise<DetectedWorkspace | undefined> {\n const patterns = await readWorkspacePatterns(projectRoot);\n if (!patterns || patterns.length === 0) return undefined;\n\n const packageDirs = await resolvePatterns(projectRoot, patterns);\n const packages = await resolvePackages(projectRoot, packageDirs);\n\n if (packages.length === 0) return undefined;\n\n // Resolve internal deps: check if any dependency name matches a workspace package\n const packageNames = new Set(packages.map((p) => p.name));\n for (const pkg of packages) {\n pkg.internalDeps = pkg.internalDeps.filter((dep) => packageNames.has(dep));\n }\n\n return { patterns, packages };\n}\n\n/**\n * Reads workspace glob patterns from pnpm-workspace.yaml or package.json.\n */\nasync function readWorkspacePatterns(projectRoot: string): Promise<string[] | undefined> {\n // Try pnpm-workspace.yaml first\n try {\n const yaml = await readFile(join(projectRoot, 'pnpm-workspace.yaml'), 'utf-8');\n return parsePnpmWorkspaceYaml(yaml);\n } catch {\n // Not found, try package.json\n }\n\n // Fall back to package.json workspaces field\n const pkg = await readPackageJson(projectRoot);\n if (!pkg) return undefined;\n\n const raw = pkg as Record<string, unknown>;\n const workspaces = raw.workspaces;\n\n if (Array.isArray(workspaces)) {\n return workspaces.filter((w): w is string => typeof w === 'string');\n }\n\n // Handle { packages: [...] } format\n if (workspaces && typeof workspaces === 'object' && 'packages' in workspaces) {\n const nested = (workspaces as { packages: unknown }).packages;\n if (Array.isArray(nested)) {\n return nested.filter((w): w is string => typeof w === 'string');\n }\n }\n\n return undefined;\n}\n\n/**\n * Parses workspace patterns from pnpm-workspace.yaml content.\n * Handles the common format: `packages:\\n - 'packages/*'`\n */\nfunction parsePnpmWorkspaceYaml(content: string): string[] {\n const patterns: string[] = [];\n let inPackages = false;\n\n for (const line of content.split('\\n')) {\n const trimmed = line.trim();\n\n if (trimmed === 'packages:') {\n inPackages = true;\n continue;\n }\n\n // Stop at next top-level key\n if (inPackages && trimmed.length > 0 && !trimmed.startsWith('-')) {\n break;\n }\n\n if (inPackages && trimmed.startsWith('-')) {\n // Extract the pattern, stripping quotes and leading dash\n const value = trimmed\n .slice(1)\n .trim()\n .replace(/^['\"]|['\"]$/g, '');\n if (value) patterns.push(value);\n }\n }\n\n return patterns;\n}\n\n/**\n * Resolves workspace glob patterns to actual directory paths.\n * Supports simple `*` wildcard matching (e.g. `packages/*`).\n */\nasync function resolvePatterns(projectRoot: string, patterns: string[]): Promise<string[]> {\n const dirs: string[] = [];\n\n for (const pattern of patterns) {\n if (pattern.endsWith('/*')) {\n // Simple wildcard: list children of the parent directory\n const parent = join(projectRoot, pattern.slice(0, -2));\n try {\n const entries = await readdir(parent, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory()) {\n dirs.push(join(parent, entry.name));\n }\n }\n } catch {\n // Parent directory doesn't exist, skip\n }\n } else {\n // Literal path\n dirs.push(join(projectRoot, pattern));\n }\n }\n\n return dirs;\n}\n\n/**\n * Resolves workspace directories to WorkspacePackage objects.\n * Skips directories without a valid package.json.\n */\nasync function resolvePackages(projectRoot: string, dirs: string[]): Promise<WorkspacePackage[]> {\n const packages: WorkspacePackage[] = [];\n\n for (const dir of dirs) {\n const pkg = await readPackageJson(dir);\n if (!pkg?.name) continue;\n\n // Collect all dependency names as potential internal deps\n const allDeps = [\n ...Object.keys(pkg.dependencies ?? {}),\n ...Object.keys(pkg.devDependencies ?? {}),\n ];\n\n packages.push({\n name: pkg.name,\n path: dir,\n relativePath: relative(projectRoot, dir),\n internalDeps: allDeps,\n });\n }\n\n return packages;\n}\n","import { stat } from 'node:fs/promises';\nimport { basename, resolve } from 'node:path';\nimport type { ScanResult } from '@viberails/types';\nimport {\n aggregateConventions,\n aggregateStacks,\n aggregateStatistics,\n aggregateStructures,\n} from './aggregate.js';\nimport { detectWorkspace } from './detect-workspace.js';\nimport { scanPackage } from './scan-package.js';\nimport { readPackageJson } from './utils/read-package-json.js';\n\n/**\n * Options for the scan function.\n */\nexport type ScanOptions = Record<string, never>;\n\n/**\n * Scans a project directory and returns a comprehensive analysis of its\n * stack, structure, conventions, and statistics.\n *\n * For monorepos, each workspace package is scanned independently and\n * results are aggregated. For single-package projects, the root is\n * scanned as a single package.\n *\n * @param projectPath - Absolute or relative path to the project root.\n * @param options - Optional scan configuration.\n * @returns Complete scan result for the project.\n * @throws If the project path does not exist or is not a directory.\n */\nexport async function scan(projectPath: string, _options?: ScanOptions): Promise<ScanResult> {\n const root = resolve(projectPath);\n\n // Validate that the path exists and is a directory\n try {\n const st = await stat(root);\n if (!st.isDirectory()) {\n throw new Error(`Project path is not a directory: ${root}`);\n }\n } catch (err) {\n if (err instanceof Error && err.message.startsWith('Project path is not')) {\n throw err;\n }\n throw new Error(`Project path does not exist: ${root}`);\n }\n\n const workspace = await detectWorkspace(root);\n\n if (workspace && workspace.packages.length > 0) {\n // Read root deps for sharing with workspace packages\n const rootPkg = await readPackageJson(root);\n const rootDeps: Record<string, string> = {\n ...rootPkg?.dependencies,\n ...rootPkg?.devDependencies,\n };\n\n // Scan each workspace package in parallel\n const packages = await Promise.all(\n workspace.packages.map((wp) => scanPackage(wp.path, wp.name, wp.relativePath, rootDeps)),\n );\n\n return {\n root,\n stack: aggregateStacks(packages),\n structure: aggregateStructures(packages),\n conventions: aggregateConventions(packages),\n statistics: aggregateStatistics(packages),\n workspace,\n packages,\n };\n }\n\n // Single-package project\n const rootPkg = await readPackageJson(root);\n const name = rootPkg?.name ?? basename(root);\n const pkg = await scanPackage(root, name, '');\n\n return {\n root,\n stack: pkg.stack,\n structure: pkg.structure,\n conventions: pkg.conventions,\n statistics: pkg.statistics,\n packages: [pkg],\n };\n}\n","import { resolve } from 'node:path';\nimport type { PackageScanResult } from '@viberails/types';\nimport { computeStatistics } from './compute-statistics.js';\nimport { detectConventions } from './detect-conventions.js';\nimport { detectStack } from './detect-stack.js';\nimport { detectStructure } from './detect-structure.js';\nimport { walkDirectory } from './utils/walk-directory.js';\n\n/**\n * Scans a single package directory and returns per-package scan results.\n *\n * Reuses all existing detector functions scoped to the package directory.\n * In monorepos, `rootDeps` provides root-level dependencies (e.g. typescript,\n * eslint) as a base layer — package-specific deps overlay on top.\n *\n * @param packagePath - Absolute path to the package directory.\n * @param name - Package name from package.json.\n * @param relativePath - Path relative to workspace root (empty string for single-package).\n * @param rootDeps - Optional root-level dependencies merged as a base layer.\n * @returns Per-package scan result.\n */\nexport async function scanPackage(\n packagePath: string,\n name: string,\n relativePath: string,\n rootDeps?: Record<string, string>,\n): Promise<PackageScanResult> {\n const root = resolve(packagePath);\n const dirs = await walkDirectory(root, 4);\n\n const [stack, structure, statistics] = await Promise.all([\n detectStack(root, rootDeps),\n detectStructure(root, dirs),\n computeStatistics(root, dirs),\n ]);\n\n const conventions = await detectConventions(root, structure, dirs);\n\n return {\n name,\n root,\n relativePath,\n stack,\n structure,\n conventions,\n statistics,\n };\n}\n"],"mappings":"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","names":["import_promises","import_node_path","import_promises","import_node_path","import_types","import_promises","import_node_path","import_promises","import_node_path","import_types","f","import_promises","import_node_path","import_promises","import_node_path","import_node_path","rootPkg"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/aggregate.ts","../src/compute-statistics.ts","../src/utils/walk-directory.ts","../src/detect-conventions.ts","../src/utils/classify-filename.ts","../src/detect-stack.ts","../src/stack-mappings.ts","../src/utils/read-package-json.ts","../src/detect-structure.ts","../src/utils/classify-directory.ts","../src/detect-workspace.ts","../src/scan.ts","../src/scan-package.ts"],"sourcesContent":["declare const __PACKAGE_VERSION__: string;\nexport const VERSION: string = __PACKAGE_VERSION__;\n\nexport {\n aggregateConventions,\n aggregateStacks,\n aggregateStatistics,\n aggregateStructures,\n} from './aggregate.js';\nexport { computeStatistics } from './compute-statistics.js';\nexport { detectConventions } from './detect-conventions.js';\nexport { detectStack, extractMajorVersion } from './detect-stack.js';\nexport { detectStructure } from './detect-structure.js';\nexport { detectWorkspace } from './detect-workspace.js';\nexport type { ScanOptions } from './scan.js';\nexport { scan } from './scan.js';\nexport { scanPackage } from './scan-package.js';\nexport type { PackageJson } from './utils/read-package-json.js';\nexport { readPackageJson } from './utils/read-package-json.js';\nexport type { WalkedDirectory } from './utils/walk-directory.js';\nexport { walkDirectory } from './utils/walk-directory.js';\n","import {\n type CodebaseStatistics,\n confidenceFromConsistency,\n type DetectedConvention,\n type DetectedStack,\n type DetectedStructure,\n type PackageScanResult,\n} from '@viberails/types';\n\n/** Framework priority order — higher-priority frameworks become the primary. */\nconst FRAMEWORK_PRIORITY = [\n 'nextjs',\n 'sveltekit',\n 'astro',\n 'expo',\n 'react-native',\n 'svelte',\n 'vue',\n 'react',\n];\n\n/**\n * Combines per-package stacks into a single aggregate stack.\n *\n * TypeScript wins over JavaScript if any package uses it.\n * The highest-priority framework becomes the primary; others go into libraries.\n */\nexport function aggregateStacks(packages: PackageScanResult[]): DetectedStack {\n if (packages.length === 1) return packages[0].stack;\n\n const tsPackage = packages.find((p) => p.stack.language.name === 'typescript');\n const language = tsPackage ? tsPackage.stack.language : packages[0].stack.language;\n\n const packageManager = packages[0].stack.packageManager;\n\n const frameworkPackages = packages.filter((p) => p.stack.framework);\n let framework: (typeof packages)[0]['stack']['framework'];\n if (frameworkPackages.length > 0) {\n frameworkPackages.sort((a, b) => {\n const aIdx = FRAMEWORK_PRIORITY.indexOf(a.stack.framework?.name ?? '');\n const bIdx = FRAMEWORK_PRIORITY.indexOf(b.stack.framework?.name ?? '');\n return (aIdx === -1 ? Infinity : aIdx) - (bIdx === -1 ? Infinity : bIdx);\n });\n framework = frameworkPackages[0].stack.framework;\n }\n\n const libraryMap = new Map<string, { name: string; version?: string }>();\n\n for (const pkg of packages) {\n if (pkg.stack.framework && pkg.stack.framework.name !== framework?.name) {\n libraryMap.set(pkg.stack.framework.name, pkg.stack.framework);\n }\n for (const lib of pkg.stack.libraries) {\n if (!libraryMap.has(lib.name)) {\n libraryMap.set(lib.name, lib);\n }\n }\n }\n\n const styling = packages.find((p) => p.stack.styling)?.stack.styling;\n const backend = packages.find((p) => p.stack.backend)?.stack.backend;\n const orm = packages.find((p) => p.stack.orm)?.stack.orm;\n const linter = packages.find((p) => p.stack.linter)?.stack.linter;\n const formatter = packages.find((p) => p.stack.formatter)?.stack.formatter;\n const testRunner = packages.find((p) => p.stack.testRunner)?.stack.testRunner;\n\n return {\n language,\n packageManager,\n framework,\n libraries: [...libraryMap.values()],\n styling,\n backend,\n orm,\n linter,\n formatter,\n testRunner,\n };\n}\n\n/**\n * Combines per-package structures into a single aggregate structure.\n *\n * Directory paths are prefixed with the package's relativePath.\n */\nexport function aggregateStructures(packages: PackageScanResult[]): DetectedStructure {\n if (packages.length === 1) return packages[0].structure;\n\n const srcDir = packages.some((p) => p.structure.srcDir) ? 'src' : undefined;\n\n const directories = packages.flatMap((pkg) =>\n pkg.structure.directories.map((dir) => ({\n ...dir,\n path: pkg.relativePath ? `${pkg.relativePath}/${dir.path}` : dir.path,\n })),\n );\n\n const testPatterns = packages\n .map((p) => p.structure.testPattern)\n .filter((t): t is DetectedConvention<string> => t !== undefined);\n\n let testPattern: DetectedConvention<string> | undefined;\n if (testPatterns.length > 0) {\n const counts = new Map<string, { count: number; pattern: DetectedConvention<string> }>();\n for (const tp of testPatterns) {\n const existing = counts.get(tp.value);\n if (existing) {\n existing.count++;\n } else {\n counts.set(tp.value, { count: 1, pattern: tp });\n }\n }\n let best = { count: 0, pattern: testPatterns[0] };\n for (const entry of counts.values()) {\n if (entry.count > best.count) best = entry;\n }\n testPattern = best.pattern;\n }\n\n return { srcDir, directories, testPattern };\n}\n\n/**\n * Combines per-package conventions into aggregate conventions.\n *\n * When all packages agree on a convention, reports it with averaged consistency.\n * When packages disagree, scales consistency by agreement ratio.\n * Omits conventions present in fewer than half of packages.\n */\nexport function aggregateConventions(\n packages: PackageScanResult[],\n): Record<string, DetectedConvention> {\n if (packages.length === 1) return packages[0].conventions;\n\n const allKeys = new Set<string>();\n for (const pkg of packages) {\n for (const key of Object.keys(pkg.conventions)) {\n allKeys.add(key);\n }\n }\n\n const result: Record<string, DetectedConvention> = {};\n\n for (const key of allKeys) {\n const entries = packages\n .map((p) => p.conventions[key])\n .filter((c): c is DetectedConvention => c !== undefined);\n\n if (entries.length < packages.length / 2) continue;\n\n const valueCounts = new Map<\n string,\n { count: number; totalConsistency: number; totalSamples: number }\n >();\n for (const entry of entries) {\n const existing = valueCounts.get(entry.value);\n if (existing) {\n existing.count++;\n existing.totalConsistency += entry.consistency;\n existing.totalSamples += entry.sampleSize;\n } else {\n valueCounts.set(entry.value, {\n count: 1,\n totalConsistency: entry.consistency,\n totalSamples: entry.sampleSize,\n });\n }\n }\n\n let majorityValue = '';\n let majorityData = { count: 0, totalConsistency: 0, totalSamples: 0 };\n for (const [value, data] of valueCounts) {\n if (data.count > majorityData.count) {\n majorityValue = value;\n majorityData = data;\n }\n }\n\n const agreement = majorityData.count / entries.length;\n const avgConsistency = majorityData.totalConsistency / majorityData.count;\n const consistency = Math.round(avgConsistency * agreement);\n\n result[key] = {\n value: majorityValue,\n confidence: confidenceFromConsistency(consistency),\n sampleSize: majorityData.totalSamples,\n consistency,\n };\n }\n\n return result;\n}\n\n/**\n * Combines per-package statistics into aggregate statistics.\n *\n * Sums totals, recomputes averages, merges largest files with path prefixing.\n */\nexport function aggregateStatistics(packages: PackageScanResult[]): CodebaseStatistics {\n if (packages.length === 1) return packages[0].statistics;\n\n const totalFiles = packages.reduce((sum, p) => sum + p.statistics.totalFiles, 0);\n const totalLines = packages.reduce((sum, p) => sum + p.statistics.totalLines, 0);\n const averageFileLines = totalFiles > 0 ? Math.round(totalLines / totalFiles) : 0;\n\n const largestFiles = packages\n .flatMap((pkg) =>\n pkg.statistics.largestFiles.map((f) => ({\n path: pkg.relativePath ? `${pkg.relativePath}/${f.path}` : f.path,\n lines: f.lines,\n })),\n )\n .sort((a, b) => b.lines - a.lines)\n .slice(0, 5);\n\n const filesByExtension: Record<string, number> = {};\n for (const pkg of packages) {\n for (const [ext, count] of Object.entries(pkg.statistics.filesByExtension)) {\n filesByExtension[ext] = (filesByExtension[ext] ?? 0) + count;\n }\n }\n\n return { totalFiles, totalLines, averageFileLines, largestFiles, filesByExtension };\n}\n","import { readdir, readFile } from 'node:fs/promises';\nimport { extname, join } from 'node:path';\nimport type { CodebaseStatistics, FileStatistic } from '@viberails/types';\nimport type { WalkedDirectory } from './utils/walk-directory.js';\nimport { SOURCE_EXTENSIONS, walkDirectory } from './utils/walk-directory.js';\n\n/**\n * Counts lines in a file by reading its contents.\n *\n * @param filePath - Absolute path to the file.\n * @returns Number of lines, or 0 if the file can't be read.\n */\nasync function countLines(filePath: string): Promise<number> {\n try {\n const content = await readFile(filePath, 'utf-8');\n if (content.length === 0) return 0;\n let count = 0;\n for (let i = 0; i < content.length; i++) {\n if (content.charCodeAt(i) === 10) count++;\n }\n // A file with no trailing newline has one more line than newline count\n if (content.charCodeAt(content.length - 1) !== 10) count++;\n return count;\n } catch {\n return 0;\n }\n}\n\n/**\n * Collects root-level source file names from the project directory.\n *\n * @param projectPath - Absolute path to the project root.\n * @returns Array of source file names in the root directory.\n */\nasync function getRootSourceFiles(projectPath: string): Promise<string[]> {\n try {\n const entries = await readdir(projectPath, { withFileTypes: true });\n const sourceFiles: string[] = [];\n for (const entry of entries) {\n if (entry.isFile()) {\n const ext = extname(entry.name);\n if (SOURCE_EXTENSIONS.has(ext)) {\n sourceFiles.push(entry.name);\n }\n }\n }\n return sourceFiles;\n } catch {\n return [];\n }\n}\n\n/**\n * Computes quantitative statistics about a project's source files.\n *\n * Reads each source file and produces aggregate metrics including\n * file counts, line counts, and extension breakdown.\n *\n * @param projectPath - Absolute path to the project root.\n * @param dirs - Pre-walked directory list. If not provided, walks the directory tree.\n * @returns Statistics about the codebase.\n */\nexport async function computeStatistics(\n projectPath: string,\n dirs?: WalkedDirectory[],\n): Promise<CodebaseStatistics> {\n const directories = dirs ?? (await walkDirectory(projectPath));\n const rootFiles = await getRootSourceFiles(projectPath);\n\n // Collect all file paths and extensions\n const filesToProcess: Array<{ relativePath: string; absolutePath: string; ext: string }> = [];\n\n // Root-level source files\n for (const name of rootFiles) {\n filesToProcess.push({\n relativePath: name,\n absolutePath: join(projectPath, name),\n ext: extname(name),\n });\n }\n\n // Files from subdirectories\n for (const dir of directories) {\n for (const name of dir.sourceFileNames) {\n filesToProcess.push({\n relativePath: `${dir.relativePath}/${name}`,\n absolutePath: join(dir.absolutePath, name),\n ext: extname(name),\n });\n }\n }\n\n const totalFiles = filesToProcess.length;\n\n if (totalFiles === 0) {\n return {\n totalFiles: 0,\n totalLines: 0,\n averageFileLines: 0,\n largestFiles: [],\n filesByExtension: {},\n };\n }\n\n // Count lines for all files concurrently\n const lineResults = await Promise.all(\n filesToProcess.map(async (file) => ({\n path: file.relativePath,\n lines: await countLines(file.absolutePath),\n ext: file.ext,\n })),\n );\n\n let totalLines = 0;\n const filesByExtension: Record<string, number> = {};\n const allFiles: FileStatistic[] = [];\n\n for (const result of lineResults) {\n totalLines += result.lines;\n filesByExtension[result.ext] = (filesByExtension[result.ext] ?? 0) + 1;\n allFiles.push({ path: result.path, lines: result.lines });\n }\n\n // Sort descending by lines, take top 5\n allFiles.sort((a, b) => b.lines - a.lines);\n const largestFiles = allFiles.slice(0, 5);\n\n return {\n totalFiles,\n totalLines,\n averageFileLines: Math.round(totalLines / totalFiles),\n largestFiles,\n filesByExtension,\n };\n}\n","import { readdir } from 'node:fs/promises';\nimport { join, relative } from 'node:path';\n\n/** Directories to always skip during scanning. */\nconst IGNORED_DIRS = new Set([\n 'node_modules',\n '.git',\n 'dist',\n 'build',\n '.next',\n '.nuxt',\n '.viberails',\n 'coverage',\n '.turbo',\n '.cache',\n '.output',\n '.expo',\n 'android',\n 'ios',\n 'Pods',\n '.gradle',\n]);\n\n/** Source file extensions to count. */\nexport const SOURCE_EXTENSIONS = new Set([\n '.ts',\n '.tsx',\n '.js',\n '.jsx',\n '.mjs',\n '.cjs',\n '.vue',\n '.svelte',\n '.astro',\n]);\n\nexport interface WalkedDirectory {\n /** Path relative to project root, using forward slashes. */\n relativePath: string;\n /** Absolute path. */\n absolutePath: string;\n /** Number of source files directly in this directory. */\n sourceFileCount: number;\n /** Names of source files in this directory. */\n sourceFileNames: string[];\n /** Depth relative to the project root (1 = direct children). */\n depth: number;\n}\n\n/**\n * Walks a project directory tree using BFS, collecting directory info.\n *\n * @param projectPath - Absolute path to the project root.\n * @param maxDepth - Maximum directory depth to traverse (default 4).\n * @returns Flat list of all visited directories (excluding root itself).\n */\nexport async function walkDirectory(\n projectPath: string,\n maxDepth: number = 4,\n): Promise<WalkedDirectory[]> {\n const results: WalkedDirectory[] = [];\n const queue: Array<{ absolutePath: string; depth: number }> = [];\n\n try {\n const rootEntries = await readdir(projectPath, { withFileTypes: true });\n for (const entry of rootEntries) {\n if (entry.isDirectory() && !entry.isSymbolicLink() && !IGNORED_DIRS.has(entry.name)) {\n queue.push({ absolutePath: join(projectPath, entry.name), depth: 1 });\n }\n }\n } catch {\n return results;\n }\n\n while (queue.length > 0) {\n const item = queue.shift();\n if (!item) break;\n const { absolutePath, depth } = item;\n const sourceFileNames: string[] = [];\n\n try {\n const entries = await readdir(absolutePath, { withFileTypes: true });\n\n for (const entry of entries) {\n if (entry.isSymbolicLink()) continue;\n\n if (entry.isDirectory() && depth < maxDepth && !IGNORED_DIRS.has(entry.name)) {\n queue.push({ absolutePath: join(absolutePath, entry.name), depth: depth + 1 });\n } else if (entry.isFile()) {\n const dotIndex = entry.name.lastIndexOf('.');\n if (dotIndex > 0) {\n const ext = entry.name.substring(dotIndex);\n if (SOURCE_EXTENSIONS.has(ext)) {\n sourceFileNames.push(entry.name);\n }\n }\n }\n }\n } catch {\n continue;\n }\n\n const rel = relative(projectPath, absolutePath).split('\\\\').join('/');\n results.push({\n relativePath: rel,\n absolutePath,\n sourceFileCount: sourceFileNames.length,\n sourceFileNames,\n depth,\n });\n }\n\n return results;\n}\n","import { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { DetectedConvention, DetectedStructure } from '@viberails/types';\nimport { confidenceFromConsistency } from '@viberails/types';\nimport { classifyFilename } from './utils/classify-filename.js';\nimport type { WalkedDirectory } from './utils/walk-directory.js';\nimport { walkDirectory } from './utils/walk-directory.js';\n\n/**\n * Detects coding conventions used in a project by analyzing file names\n * and configuration files.\n *\n * @param projectPath - Absolute path to the project root directory.\n * @param structure - Previously detected directory structure, used to identify\n * directories by role.\n * @param dirs - Pre-walked directory list. If not provided, walks the directory tree.\n * @returns A record of detected conventions keyed by convention name.\n * Only statistical conventions with sampleSize >= 3 are included.\n */\nexport async function detectConventions(\n projectPath: string,\n structure: DetectedStructure,\n dirs?: WalkedDirectory[],\n): Promise<Record<string, DetectedConvention>> {\n if (!dirs) {\n dirs = await walkDirectory(projectPath, 4);\n }\n\n const result: Record<string, DetectedConvention> = {};\n\n const fileNaming = detectFileNaming(dirs);\n if (fileNaming) result.fileNaming = fileNaming;\n\n const componentNaming = detectComponentNaming(dirs, structure);\n if (componentNaming) result.componentNaming = componentNaming;\n\n const hookNaming = detectHookNaming(dirs, structure);\n if (hookNaming) result.hookNaming = hookNaming;\n\n // importAlias is binary (present or not) — bypasses sampleSize threshold\n const importAlias = await detectImportAlias(projectPath);\n if (importAlias) result.importAlias = importAlias;\n\n return result;\n}\n\n/** Strips the file extension, handling multi-dot names like foo.test.ts. */\nfunction stripExtension(filename: string): string {\n const firstDot = filename.indexOf('.');\n return firstDot > 0 ? filename.substring(0, firstDot) : filename;\n}\n\n/**\n * Detects the dominant file naming convention across all directories\n * with 3+ source files. Only returns conventions with consistency >= 70%\n * (medium or high confidence).\n */\nfunction detectFileNaming(dirs: WalkedDirectory[]): DetectedConvention | undefined {\n const conventionCounts = new Map<string, number>();\n let total = 0;\n\n for (const dir of dirs) {\n if (dir.sourceFileCount < 3) continue;\n\n for (const filename of dir.sourceFileNames) {\n if (filename.includes('.test.') || filename.includes('.spec.')) continue;\n const bare = stripExtension(filename);\n if (bare === 'index') continue;\n\n const convention = classifyFilename(bare);\n total++;\n if (convention !== 'unknown') {\n conventionCounts.set(convention, (conventionCounts.get(convention) ?? 0) + 1);\n }\n }\n }\n\n if (total < 3) return undefined;\n\n const sorted = [...conventionCounts.entries()].sort((a, b) => b[1] - a[1]);\n if (sorted.length === 0) return undefined;\n\n const [dominantConvention, dominantCount] = sorted[0];\n const consistency = Math.round((dominantCount / total) * 100);\n const confidence = confidenceFromConsistency(consistency);\n\n if (confidence === 'low') return undefined;\n\n return {\n value: dominantConvention,\n confidence,\n sampleSize: total,\n consistency,\n };\n}\n\n/**\n * Detects component naming convention by checking if .tsx files in\n * component directories use PascalCase filenames.\n */\nfunction detectComponentNaming(\n dirs: WalkedDirectory[],\n structure: DetectedStructure,\n): DetectedConvention | undefined {\n const componentPaths = new Set(\n structure.directories.filter((d) => d.role === 'components').map((d) => d.path),\n );\n\n const tsxFiles: string[] = [];\n for (const dir of dirs) {\n if (!componentPaths.has(dir.relativePath)) continue;\n for (const f of dir.sourceFileNames) {\n if (f.endsWith('.tsx')) tsxFiles.push(f);\n }\n }\n\n if (tsxFiles.length < 3) return undefined;\n\n const pascalCount = tsxFiles.filter((f) => /^[A-Z]/.test(stripExtension(f))).length;\n const consistency = Math.round((pascalCount / tsxFiles.length) * 100);\n const dominantValue = pascalCount >= tsxFiles.length / 2 ? 'PascalCase' : 'camelCase';\n\n return {\n value: dominantValue,\n confidence: confidenceFromConsistency(consistency),\n sampleSize: tsxFiles.length,\n consistency,\n };\n}\n\n/**\n * Detects hook naming convention by checking if hook files use\n * kebab-case (use-*) or camelCase (useXxx) prefix.\n */\nfunction detectHookNaming(\n dirs: WalkedDirectory[],\n structure: DetectedStructure,\n): DetectedConvention | undefined {\n const hookPaths = new Set(\n structure.directories.filter((d) => d.role === 'hooks').map((d) => d.path),\n );\n\n const hookFiles: string[] = [];\n for (const dir of dirs) {\n if (!hookPaths.has(dir.relativePath)) continue;\n for (const f of dir.sourceFileNames) {\n const bare = stripExtension(f);\n if (bare.startsWith('use-') || /^use[A-Z]/.test(bare)) {\n hookFiles.push(f);\n }\n }\n }\n\n if (hookFiles.length < 3) return undefined;\n\n let kebabCount = 0;\n let camelCount = 0;\n\n for (const filename of hookFiles) {\n const bare = stripExtension(filename);\n if (bare.startsWith('use-')) {\n kebabCount++;\n } else if (/^use[A-Z]/.test(bare)) {\n camelCount++;\n }\n }\n\n const total = hookFiles.length;\n const isDominantKebab = kebabCount >= camelCount;\n const dominantCount = isDominantKebab ? kebabCount : camelCount;\n const consistency = Math.round((dominantCount / total) * 100);\n\n return {\n value: isDominantKebab ? 'use-*' : 'useXxx',\n confidence: confidenceFromConsistency(consistency),\n sampleSize: total,\n consistency,\n };\n}\n\n/** Minimal shape for the tsconfig subset we read. */\ninterface TsConfigSubset {\n compilerOptions?: {\n paths?: Record<string, string[]>;\n };\n}\n\n/**\n * Detects import alias patterns from tsconfig.json or jsconfig.json paths configuration.\n * Checks tsconfig.json first, then falls back to jsconfig.json for JavaScript projects.\n */\nasync function detectImportAlias(projectPath: string): Promise<DetectedConvention | undefined> {\n for (const configFile of ['tsconfig.json', 'jsconfig.json']) {\n try {\n const raw = await readFile(join(projectPath, configFile), 'utf-8');\n const config = JSON.parse(raw) as TsConfigSubset;\n const paths = config.compilerOptions?.paths;\n if (!paths) continue;\n\n const aliases = Object.keys(paths);\n if (aliases.length === 0) continue;\n\n return {\n value: aliases.join(','),\n confidence: 'high',\n sampleSize: aliases.length,\n consistency: 100,\n };\n } catch {}\n }\n return undefined;\n}\n","/**\n * Naming convention types for filenames.\n */\nexport type FilenameConvention =\n | 'kebab-case'\n | 'camelCase'\n | 'PascalCase'\n | 'snake_case'\n | 'unknown';\n\nconst PATTERNS = [\n { convention: 'PascalCase', regex: /^[A-Z][a-zA-Z0-9]*$/ },\n { convention: 'camelCase', regex: /^[a-z][a-zA-Z0-9]*[A-Z][a-zA-Z0-9]*$/ },\n { convention: 'kebab-case', regex: /^[a-z][a-z0-9]*(-[a-z0-9]+)+$/ },\n { convention: 'snake_case', regex: /^[a-z][a-z0-9]*(_[a-z0-9]+)+$/ },\n] as const;\n\n/**\n * Classifies a filename (without extension) into a naming convention.\n *\n * Single lowercase words like 'utils' return 'unknown' because they are\n * ambiguous — they could be kebab-case, snake_case, or camelCase without\n * a disambiguating structural marker.\n *\n * @param filename - The bare filename with no extension (e.g. 'user-profile').\n * @returns The detected naming convention, or 'unknown' if ambiguous.\n */\nexport function classifyFilename(filename: string): FilenameConvention {\n for (const { convention, regex } of PATTERNS) {\n if (regex.test(filename)) return convention;\n }\n return 'unknown';\n}\n","import { access } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { DetectedStack, StackItem } from '@viberails/types';\nimport {\n BACKEND_MAPPINGS,\n FRAMEWORK_MAPPINGS,\n LIBRARY_MAPPINGS,\n LOCK_FILE_MAP,\n ORM_MAPPINGS,\n STYLING_MAPPINGS,\n} from './stack-mappings.js';\nimport { readPackageJson } from './utils/read-package-json.js';\n\n/**\n * Extracts the major version number from a semver range string.\n *\n * @param range - A semver range such as `\"^15.0.3\"`, `\"~2.1.0\"`, or `\"3.x\"`.\n * @returns The major version string (e.g. `\"15\"`), or `undefined` if extraction fails.\n */\nexport function extractMajorVersion(range: string): string | undefined {\n const match = range.match(/(\\d+)/);\n return match?.[1];\n}\n\n/** Check whether a file exists at the given path. */\nasync function fileExists(filePath: string): Promise<boolean> {\n try {\n await access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Detects the technology stack of a project by reading its package.json\n * and checking for lock files and configuration files.\n *\n * When `additionalDeps` is provided, they are merged as a base layer\n * beneath the package's own deps. This allows monorepo root-level deps\n * (e.g. typescript, eslint) to be visible during per-package scanning.\n *\n * @param projectPath - Absolute path to the project root directory.\n * @param additionalDeps - Optional base dependencies merged under package deps.\n * @returns The detected technology stack.\n */\nexport async function detectStack(\n projectPath: string,\n additionalDeps?: Record<string, string>,\n): Promise<DetectedStack> {\n const pkg = await readPackageJson(projectPath);\n const allDeps: Record<string, string> = {\n ...additionalDeps,\n ...pkg?.dependencies,\n ...pkg?.devDependencies,\n };\n\n const framework = detectFramework(allDeps);\n const language = await detectLanguage(projectPath, allDeps);\n const styling = detectFirst(allDeps, STYLING_MAPPINGS);\n const backend = detectFirst(allDeps, BACKEND_MAPPINGS);\n const orm = detectFirst(allDeps, ORM_MAPPINGS);\n const packageManager = await detectPackageManager(projectPath);\n const linter = detectLinter(allDeps);\n const formatter = detectFormatter(allDeps);\n const testRunner = detectTestRunner(allDeps);\n const libraries = detectLibraries(allDeps);\n\n return {\n ...(framework && { framework }),\n language,\n ...(styling && { styling }),\n ...(backend && { backend }),\n ...(orm && { orm }),\n packageManager,\n ...(linter && { linter }),\n ...(formatter && { formatter }),\n ...(testRunner && { testRunner }),\n libraries,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Individual detectors\n// ---------------------------------------------------------------------------\n\nfunction detectFramework(allDeps: Record<string, string>): StackItem | undefined {\n for (const mapping of FRAMEWORK_MAPPINGS) {\n if (!(mapping.dep in allDeps)) continue;\n if (mapping.excludeDeps?.some((dep) => dep in allDeps)) continue;\n return {\n name: mapping.name,\n version: extractMajorVersion(allDeps[mapping.dep]),\n };\n }\n return undefined;\n}\n\nasync function detectLanguage(\n projectPath: string,\n allDeps: Record<string, string>,\n): Promise<StackItem> {\n if ('typescript' in allDeps) {\n return {\n name: 'typescript',\n version: extractMajorVersion(allDeps.typescript),\n };\n }\n if (await fileExists(join(projectPath, 'tsconfig.json'))) {\n return { name: 'typescript' };\n }\n return { name: 'javascript' };\n}\n\nfunction detectFirst(\n allDeps: Record<string, string>,\n mappings: Array<{ dep: string; name: string }>,\n): StackItem | undefined {\n for (const mapping of mappings) {\n if (mapping.dep in allDeps) {\n return {\n name: mapping.name,\n version: extractMajorVersion(allDeps[mapping.dep]),\n };\n }\n }\n return undefined;\n}\n\nasync function detectPackageManager(projectPath: string): Promise<StackItem> {\n for (const entry of LOCK_FILE_MAP) {\n if (await fileExists(join(projectPath, entry.file))) {\n return { name: entry.name };\n }\n }\n return { name: 'npm' };\n}\n\nfunction detectLinter(allDeps: Record<string, string>): StackItem | undefined {\n if ('eslint' in allDeps) {\n return { name: 'eslint', version: extractMajorVersion(allDeps.eslint) };\n }\n if ('@biomejs/biome' in allDeps) {\n return {\n name: 'biome',\n version: extractMajorVersion(allDeps['@biomejs/biome']),\n };\n }\n return undefined;\n}\n\nfunction detectFormatter(allDeps: Record<string, string>): StackItem | undefined {\n if ('prettier' in allDeps) {\n return { name: 'prettier', version: extractMajorVersion(allDeps.prettier) };\n }\n if ('@biomejs/biome' in allDeps) {\n return {\n name: 'biome',\n version: extractMajorVersion(allDeps['@biomejs/biome']),\n };\n }\n return undefined;\n}\n\nfunction detectTestRunner(allDeps: Record<string, string>): StackItem | undefined {\n if ('vitest' in allDeps) {\n return { name: 'vitest', version: extractMajorVersion(allDeps.vitest) };\n }\n if ('jest' in allDeps) {\n return { name: 'jest', version: extractMajorVersion(allDeps.jest) };\n }\n if ('@playwright/test' in allDeps) {\n return { name: 'playwright', version: extractMajorVersion(allDeps['@playwright/test']) };\n }\n if ('cypress' in allDeps) {\n return { name: 'cypress', version: extractMajorVersion(allDeps.cypress) };\n }\n if ('mocha' in allDeps) {\n return { name: 'mocha', version: extractMajorVersion(allDeps.mocha) };\n }\n return undefined;\n}\n\nfunction detectLibraries(allDeps: Record<string, string>): StackItem[] {\n const libs: StackItem[] = [];\n for (const mapping of LIBRARY_MAPPINGS) {\n const found = mapping.deps.find((dep) => dep in allDeps);\n if (found) {\n libs.push({\n name: mapping.name,\n version: extractMajorVersion(allDeps[found]),\n });\n }\n }\n return libs;\n}\n","/**\n * Detection mapping tables used by detectStack to identify frameworks,\n * backends, ORMs, styling solutions, and notable libraries from package.json.\n */\n\nexport interface FrameworkMapping {\n /** Package name to look for in dependencies. */\n dep: string;\n /** Name to use in the StackItem. */\n name: string;\n /** If any of these deps are present, skip this mapping. */\n excludeDeps?: string[];\n}\n\nexport const FRAMEWORK_MAPPINGS: FrameworkMapping[] = [\n { dep: 'next', name: 'nextjs' },\n { dep: 'expo', name: 'expo' },\n { dep: 'react-native', name: 'react-native', excludeDeps: ['expo'] },\n { dep: '@angular/core', name: 'angular' },\n { dep: '@sveltejs/kit', name: 'sveltekit' },\n { dep: 'svelte', name: 'svelte', excludeDeps: ['@sveltejs/kit', 'astro'] },\n { dep: 'astro', name: 'astro' },\n { dep: '@remix-run/react', name: 'remix' },\n { dep: 'nuxt', name: 'nuxt' },\n { dep: 'vue', name: 'vue', excludeDeps: ['nuxt'] },\n { dep: 'gatsby', name: 'gatsby' },\n { dep: 'solid-js', name: 'solidjs' },\n { dep: '@builder.io/qwik', name: 'qwik' },\n { dep: 'electron', name: 'electron' },\n { dep: '@tauri-apps/api', name: 'tauri' },\n {\n dep: 'react',\n name: 'react',\n excludeDeps: ['next', '@remix-run/react', 'gatsby', 'expo'],\n },\n];\n\nexport const BACKEND_MAPPINGS: Array<{ dep: string; name: string }> = [\n { dep: '@nestjs/core', name: 'nestjs' },\n { dep: 'express', name: 'express' },\n { dep: 'fastify', name: 'fastify' },\n { dep: 'koa', name: 'koa' },\n { dep: 'hono', name: 'hono' },\n { dep: '@supabase/supabase-js', name: 'supabase' },\n { dep: 'firebase', name: 'firebase' },\n { dep: 'convex', name: 'convex' },\n];\n\nexport const ORM_MAPPINGS: Array<{ dep: string; name: string }> = [\n { dep: '@prisma/client', name: 'prisma' },\n { dep: 'prisma', name: 'prisma' },\n { dep: 'drizzle-orm', name: 'drizzle' },\n { dep: 'typeorm', name: 'typeorm' },\n { dep: 'sequelize', name: 'sequelize' },\n { dep: 'mongoose', name: 'mongoose' },\n { dep: 'kysely', name: 'kysely' },\n { dep: '@mikro-orm/core', name: 'mikro-orm' },\n];\n\nexport const STYLING_MAPPINGS: Array<{ dep: string; name: string }> = [\n { dep: 'tailwindcss', name: 'tailwindcss' },\n { dep: 'styled-components', name: 'styled-components' },\n { dep: '@emotion/react', name: 'emotion' },\n { dep: 'sass', name: 'sass' },\n { dep: '@vanilla-extract/css', name: 'vanilla-extract' },\n { dep: 'unocss', name: 'unocss' },\n { dep: '@pandacss/dev', name: 'panda-css' },\n { dep: 'nativewind', name: 'nativewind' },\n];\n\nexport const LIBRARY_MAPPINGS: Array<{ deps: string[]; name: string }> = [\n // Validation\n { deps: ['zod'], name: 'zod' },\n // API\n { deps: ['@trpc/server'], name: 'trpc' },\n { deps: ['@tanstack/react-query'], name: 'react-query' },\n { deps: ['@apollo/client'], name: 'apollo' },\n { deps: ['urql'], name: 'urql' },\n { deps: ['graphql'], name: 'graphql' },\n // State management\n { deps: ['@reduxjs/toolkit'], name: 'redux-toolkit' },\n { deps: ['zustand'], name: 'zustand' },\n { deps: ['jotai'], name: 'jotai' },\n { deps: ['recoil'], name: 'recoil' },\n { deps: ['mobx'], name: 'mobx' },\n { deps: ['xstate'], name: 'xstate' },\n { deps: ['valtio'], name: 'valtio' },\n // Forms\n { deps: ['react-hook-form'], name: 'react-hook-form' },\n { deps: ['formik'], name: 'formik' },\n // HTTP\n { deps: ['axios'], name: 'axios' },\n // Auth\n { deps: ['next-auth'], name: 'next-auth' },\n { deps: ['@auth/core'], name: 'auth-js' },\n { deps: ['@clerk/nextjs'], name: 'clerk' },\n { deps: ['lucia'], name: 'lucia' },\n // Dates\n { deps: ['date-fns'], name: 'date-fns' },\n { deps: ['dayjs'], name: 'dayjs' },\n { deps: ['luxon'], name: 'luxon' },\n // i18n\n { deps: ['i18next'], name: 'i18next' },\n { deps: ['next-i18next'], name: 'next-i18next' },\n // Payments\n { deps: ['stripe'], name: 'stripe' },\n // Realtime\n { deps: ['socket.io'], name: 'socket.io' },\n // Testing utilities\n { deps: ['@testing-library/react'], name: 'testing-library' },\n { deps: ['msw'], name: 'msw' },\n { deps: ['storybook', '@storybook/react'], name: 'storybook' },\n // Bundlers\n { deps: ['vite'], name: 'vite' },\n { deps: ['webpack'], name: 'webpack' },\n { deps: ['esbuild'], name: 'esbuild' },\n { deps: ['@rspack/core'], name: 'rspack' },\n // Monorepo\n { deps: ['nx'], name: 'nx' },\n { deps: ['lerna'], name: 'lerna' },\n];\n\nexport const LOCK_FILE_MAP: Array<{ file: string; name: string }> = [\n { file: 'pnpm-lock.yaml', name: 'pnpm' },\n { file: 'yarn.lock', name: 'yarn' },\n { file: 'bun.lockb', name: 'bun' },\n { file: 'package-lock.json', name: 'npm' },\n];\n","import { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\n/**\n * Minimal interface for the fields we read from package.json.\n */\nexport interface PackageJson {\n name?: string;\n version?: string;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n}\n\n/**\n * Safely reads and parses a package.json file from the given directory.\n *\n * @param projectPath - Absolute path to the project root directory.\n * @returns Parsed package.json contents, or `null` if the file doesn't exist or is invalid JSON.\n */\nexport async function readPackageJson(projectPath: string): Promise<PackageJson | null> {\n try {\n const raw = await readFile(join(projectPath, 'package.json'), 'utf-8');\n return JSON.parse(raw) as PackageJson;\n } catch {\n return null;\n }\n}\n","import type { DetectedConvention, DetectedStructure } from '@viberails/types';\nimport { confidenceFromConsistency } from '@viberails/types';\nimport { classifyDirectory } from './utils/classify-directory.js';\nimport type { WalkedDirectory } from './utils/walk-directory.js';\nimport { walkDirectory } from './utils/walk-directory.js';\n\n/**\n * Detects the directory structure and organization of a project.\n *\n * Classifies directories by role, detects whether a src/ directory is in use,\n * and identifies test file patterns.\n *\n * @param projectPath - Absolute path to the project root directory.\n * @param dirs - Pre-walked directory list. If not provided, walks the directory tree.\n * @returns The detected directory structure.\n */\nexport async function detectStructure(\n projectPath: string,\n dirs?: WalkedDirectory[],\n): Promise<DetectedStructure> {\n if (!dirs) {\n dirs = await walkDirectory(projectPath, 4);\n }\n\n // Detect srcDir\n const hasSrcDir = dirs.some((d) => d.relativePath === 'src' || d.relativePath.startsWith('src/'));\n const srcDir = hasSrcDir ? 'src' : undefined;\n\n // Classify directories\n const directories = dirs.map((d) => classifyDirectory(d)).filter((d) => d !== null);\n\n // Detect test pattern\n const testPattern = detectTestPattern(dirs);\n\n return {\n ...(srcDir !== undefined && { srcDir }),\n directories,\n ...(testPattern !== undefined && { testPattern }),\n };\n}\n\n/**\n * Detects the dominant test file naming pattern from all source files.\n * Returns undefined if fewer than 3 test files are found.\n */\nfunction detectTestPattern(\n dirs: Array<{ sourceFileNames: string[] }>,\n): DetectedConvention<string> | undefined {\n const allFiles = dirs.flatMap((d) => d.sourceFileNames);\n const testFiles = allFiles.filter((f) => f.includes('.test.') || f.includes('.spec.'));\n\n if (testFiles.length < 3) return undefined;\n\n const dotTestCount = testFiles.filter((f) => f.includes('.test.')).length;\n const dotSpecCount = testFiles.filter((f) => f.includes('.spec.')).length;\n\n const isDotTest = dotTestCount >= dotSpecCount;\n const dominantSep = isDotTest ? '.test.' : '.spec.';\n const dominantCount = isDotTest ? dotTestCount : dotSpecCount;\n const consistency = Math.round((dominantCount / testFiles.length) * 100);\n\n // Find most common extension among dominant test files\n const extCounts = new Map<string, number>();\n for (const f of testFiles.filter((f) => f.includes(dominantSep))) {\n const ext = f.substring(f.lastIndexOf('.'));\n extCounts.set(ext, (extCounts.get(ext) ?? 0) + 1);\n }\n const topExt = [...extCounts.entries()].sort((a, b) => b[1] - a[1])[0]?.[0] ?? '.ts';\n\n const sep = isDotTest ? 'test' : 'spec';\n\n return {\n value: `*.${sep}${topExt}`,\n confidence: confidenceFromConsistency(consistency),\n sampleSize: testFiles.length,\n consistency,\n };\n}\n","import type { Confidence, DirectoryRole } from '@viberails/types';\nimport type { WalkedDirectory } from './walk-directory.js';\n\nexport interface ClassifiedDirectory {\n path: string;\n role: DirectoryRole;\n fileCount: number;\n confidence: Confidence;\n}\n\ninterface RolePattern {\n role: DirectoryRole;\n pathPatterns: string[];\n}\n\nconst ROLE_PATTERNS: RolePattern[] = [\n { role: 'pages', pathPatterns: ['src/app', 'src/pages', 'app', 'pages'] },\n { role: 'components', pathPatterns: ['src/components', 'components'] },\n { role: 'hooks', pathPatterns: ['src/hooks', 'hooks'] },\n {\n role: 'utils',\n pathPatterns: ['src/lib', 'src/utils', 'src/helpers', 'lib', 'utils', 'helpers'],\n },\n { role: 'types', pathPatterns: ['src/types', 'types', 'src/@types', '@types'] },\n { role: 'tests', pathPatterns: ['__tests__', 'tests', 'test', 'src/__tests__', 'src/tests'] },\n { role: 'styles', pathPatterns: ['src/styles', 'styles', 'src/css', 'css'] },\n { role: 'api', pathPatterns: ['src/api', 'api', 'src/app/api', 'app/api'] },\n { role: 'config', pathPatterns: ['config', 'src/config'] },\n];\n\n/**\n * Classifies a directory by its role in the project.\n *\n * @param dir - A walked directory entry.\n * @returns Classified directory info, or null if the directory should be skipped.\n */\nexport function classifyDirectory(dir: WalkedDirectory): ClassifiedDirectory | null {\n // Try name-based matching first\n const nameMatch = matchByName(dir.relativePath);\n if (nameMatch) {\n const confidence: Confidence = dir.sourceFileCount > 0 ? 'high' : 'low';\n return {\n path: dir.relativePath,\n role: nameMatch,\n fileCount: dir.sourceFileCount,\n confidence,\n };\n }\n\n // Skip directories with no source files if no name match\n if (dir.sourceFileCount === 0) return null;\n\n // Content-based heuristics\n const contentRole = inferFromContent(dir);\n if (contentRole) return contentRole;\n\n // Has source files but no classification\n return {\n path: dir.relativePath,\n role: 'unknown',\n fileCount: dir.sourceFileCount,\n confidence: 'low',\n };\n}\n\n/**\n * Matches a relative path against known role patterns.\n * Supports suffix matching so monorepo paths like `apps/web/lib`\n * match patterns like `lib`.\n * Returns the role if matched, or null.\n */\nfunction matchByName(relativePath: string): DirectoryRole | null {\n for (const { role, pathPatterns } of ROLE_PATTERNS) {\n for (const pattern of pathPatterns) {\n if (relativePath === pattern || relativePath.endsWith(`/${pattern}`)) {\n return role;\n }\n }\n }\n return null;\n}\n\n/**\n * Infers directory role from file contents/names.\n */\nfunction inferFromContent(dir: WalkedDirectory): ClassifiedDirectory | null {\n const { sourceFileNames, sourceFileCount } = dir;\n\n // Check for hook files (use-* kebab or useXxx camelCase prefix)\n // Require at least 2 matching files to avoid misclassifying directories\n // with a single hook utility (e.g. lib/ with one useXxx file)\n const hookFiles = sourceFileNames.filter((f) => {\n const name = f.split('.')[0];\n return name.startsWith('use-') || /^use[A-Z]/.test(name);\n });\n if (hookFiles.length >= 2 && hookFiles.length / sourceFileCount >= 0.5) {\n return {\n path: dir.relativePath,\n role: 'hooks',\n fileCount: sourceFileCount,\n confidence: hookFiles.length / sourceFileCount >= 0.9 ? 'high' : 'medium',\n };\n }\n\n // Check for test files — require at least 2 to avoid false positives\n const testFiles = sourceFileNames.filter((f) => f.includes('.test.') || f.includes('.spec.'));\n if (testFiles.length >= 2 && testFiles.length / sourceFileCount >= 0.5) {\n return {\n path: dir.relativePath,\n role: 'tests',\n fileCount: sourceFileCount,\n confidence: testFiles.length / sourceFileCount >= 0.9 ? 'high' : 'medium',\n };\n }\n\n return null;\n}\n","import { readdir, readFile } from 'node:fs/promises';\nimport { join, relative } from 'node:path';\nimport type { DetectedWorkspace, WorkspacePackage } from '@viberails/types';\nimport { readPackageJson } from './utils/read-package-json.js';\n\n/**\n * Detects workspace configuration for monorepo projects.\n *\n * Checks for `pnpm-workspace.yaml` first, then falls back to\n * `package.json` `workspaces` field. Returns `undefined` for\n * single-package projects.\n *\n * @param projectRoot - Absolute path to the project root.\n * @returns Detected workspace info, or `undefined` if not a monorepo.\n */\nexport async function detectWorkspace(projectRoot: string): Promise<DetectedWorkspace | undefined> {\n const patterns = await readWorkspacePatterns(projectRoot);\n if (!patterns || patterns.length === 0) return undefined;\n\n const packageDirs = await resolvePatterns(projectRoot, patterns);\n const packages = await resolvePackages(projectRoot, packageDirs);\n\n if (packages.length === 0) return undefined;\n\n // Resolve internal deps: check if any dependency name matches a workspace package\n const packageNames = new Set(packages.map((p) => p.name));\n for (const pkg of packages) {\n pkg.internalDeps = pkg.internalDeps.filter((dep) => packageNames.has(dep));\n }\n\n return { patterns, packages };\n}\n\n/**\n * Reads workspace glob patterns from pnpm-workspace.yaml or package.json.\n */\nasync function readWorkspacePatterns(projectRoot: string): Promise<string[] | undefined> {\n // Try pnpm-workspace.yaml first\n try {\n const yaml = await readFile(join(projectRoot, 'pnpm-workspace.yaml'), 'utf-8');\n return parsePnpmWorkspaceYaml(yaml);\n } catch {\n // Not found, try package.json\n }\n\n // Fall back to package.json workspaces field\n const pkg = await readPackageJson(projectRoot);\n if (!pkg) return undefined;\n\n const raw = pkg as Record<string, unknown>;\n const workspaces = raw.workspaces;\n\n if (Array.isArray(workspaces)) {\n return workspaces.filter((w): w is string => typeof w === 'string');\n }\n\n // Handle { packages: [...] } format\n if (workspaces && typeof workspaces === 'object' && 'packages' in workspaces) {\n const nested = (workspaces as { packages: unknown }).packages;\n if (Array.isArray(nested)) {\n return nested.filter((w): w is string => typeof w === 'string');\n }\n }\n\n return undefined;\n}\n\n/**\n * Parses workspace patterns from pnpm-workspace.yaml content.\n * Handles the common format: `packages:\\n - 'packages/*'`\n */\nfunction parsePnpmWorkspaceYaml(content: string): string[] {\n const patterns: string[] = [];\n let inPackages = false;\n\n for (const line of content.split('\\n')) {\n const trimmed = line.trim();\n\n if (trimmed === 'packages:') {\n inPackages = true;\n continue;\n }\n\n // Stop at next top-level key\n if (inPackages && trimmed.length > 0 && !trimmed.startsWith('-')) {\n break;\n }\n\n if (inPackages && trimmed.startsWith('-')) {\n // Extract the pattern, stripping quotes and leading dash\n const value = trimmed\n .slice(1)\n .trim()\n .replace(/^['\"]|['\"]$/g, '');\n if (value) patterns.push(value);\n }\n }\n\n return patterns;\n}\n\n/**\n * Resolves workspace glob patterns to actual directory paths.\n * Supports simple `*` wildcard matching (e.g. `packages/*`).\n */\nasync function resolvePatterns(projectRoot: string, patterns: string[]): Promise<string[]> {\n const dirs: string[] = [];\n\n for (const pattern of patterns) {\n if (pattern.endsWith('/*')) {\n // Simple wildcard: list children of the parent directory\n const parent = join(projectRoot, pattern.slice(0, -2));\n try {\n const entries = await readdir(parent, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory()) {\n dirs.push(join(parent, entry.name));\n }\n }\n } catch {\n // Parent directory doesn't exist, skip\n }\n } else {\n // Literal path\n dirs.push(join(projectRoot, pattern));\n }\n }\n\n return dirs;\n}\n\n/**\n * Resolves workspace directories to WorkspacePackage objects.\n * Skips directories without a valid package.json.\n */\nasync function resolvePackages(projectRoot: string, dirs: string[]): Promise<WorkspacePackage[]> {\n const packages: WorkspacePackage[] = [];\n\n for (const dir of dirs) {\n const pkg = await readPackageJson(dir);\n if (!pkg?.name) continue;\n\n // Collect all dependency names as potential internal deps\n const allDeps = [\n ...Object.keys(pkg.dependencies ?? {}),\n ...Object.keys(pkg.devDependencies ?? {}),\n ];\n\n packages.push({\n name: pkg.name,\n path: dir,\n relativePath: relative(projectRoot, dir),\n internalDeps: allDeps,\n });\n }\n\n return packages;\n}\n","import { stat } from 'node:fs/promises';\nimport { basename, resolve } from 'node:path';\nimport type { ScanResult } from '@viberails/types';\nimport {\n aggregateConventions,\n aggregateStacks,\n aggregateStatistics,\n aggregateStructures,\n} from './aggregate.js';\nimport { detectWorkspace } from './detect-workspace.js';\nimport { scanPackage } from './scan-package.js';\nimport { readPackageJson } from './utils/read-package-json.js';\n\n/**\n * Options for the scan function.\n */\nexport type ScanOptions = Record<string, never>;\n\n/**\n * Scans a project directory and returns a comprehensive analysis of its\n * stack, structure, conventions, and statistics.\n *\n * For monorepos, each workspace package is scanned independently and\n * results are aggregated. For single-package projects, the root is\n * scanned as a single package.\n *\n * @param projectPath - Absolute or relative path to the project root.\n * @param options - Optional scan configuration.\n * @returns Complete scan result for the project.\n * @throws If the project path does not exist or is not a directory.\n */\nexport async function scan(projectPath: string, _options?: ScanOptions): Promise<ScanResult> {\n const root = resolve(projectPath);\n\n // Validate that the path exists and is a directory\n try {\n const st = await stat(root);\n if (!st.isDirectory()) {\n throw new Error(`Project path is not a directory: ${root}`);\n }\n } catch (err) {\n if (err instanceof Error && err.message.startsWith('Project path is not')) {\n throw err;\n }\n throw new Error(`Project path does not exist: ${root}`);\n }\n\n const workspace = await detectWorkspace(root);\n\n if (workspace && workspace.packages.length > 0) {\n // Read root deps for sharing with workspace packages\n const rootPkg = await readPackageJson(root);\n const rootDeps: Record<string, string> = {\n ...rootPkg?.dependencies,\n ...rootPkg?.devDependencies,\n };\n\n // Scan each workspace package in parallel\n const packages = await Promise.all(\n workspace.packages.map((wp) => scanPackage(wp.path, wp.name, wp.relativePath, rootDeps)),\n );\n\n return {\n root,\n stack: aggregateStacks(packages),\n structure: aggregateStructures(packages),\n conventions: aggregateConventions(packages),\n statistics: aggregateStatistics(packages),\n workspace,\n packages,\n };\n }\n\n // Single-package project\n const rootPkg = await readPackageJson(root);\n const name = rootPkg?.name ?? basename(root);\n const pkg = await scanPackage(root, name, '');\n\n return {\n root,\n stack: pkg.stack,\n structure: pkg.structure,\n conventions: pkg.conventions,\n statistics: pkg.statistics,\n packages: [pkg],\n };\n}\n","import { resolve } from 'node:path';\nimport type { PackageScanResult } from '@viberails/types';\nimport { computeStatistics } from './compute-statistics.js';\nimport { detectConventions } from './detect-conventions.js';\nimport { detectStack } from './detect-stack.js';\nimport { detectStructure } from './detect-structure.js';\nimport { walkDirectory } from './utils/walk-directory.js';\n\n/**\n * Scans a single package directory and returns per-package scan results.\n *\n * Reuses all existing detector functions scoped to the package directory.\n * In monorepos, `rootDeps` provides root-level dependencies (e.g. typescript,\n * eslint) as a base layer — package-specific deps overlay on top.\n *\n * @param packagePath - Absolute path to the package directory.\n * @param name - Package name from package.json.\n * @param relativePath - Path relative to workspace root (empty string for single-package).\n * @param rootDeps - Optional root-level dependencies merged as a base layer.\n * @returns Per-package scan result.\n */\nexport async function scanPackage(\n packagePath: string,\n name: string,\n relativePath: string,\n rootDeps?: Record<string, string>,\n): Promise<PackageScanResult> {\n const root = resolve(packagePath);\n const dirs = await walkDirectory(root, 4);\n\n const [stack, structure, statistics] = await Promise.all([\n detectStack(root, rootDeps),\n detectStructure(root, dirs),\n computeStatistics(root, dirs),\n ]);\n\n const conventions = await detectConventions(root, structure, dirs);\n\n return {\n name,\n root,\n relativePath,\n stack,\n structure,\n conventions,\n statistics,\n };\n}\n"],"mappings":"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","names":["import_promises","import_node_path","import_promises","import_node_path","import_types","import_promises","import_node_path","import_promises","import_node_path","import_types","f","import_promises","import_node_path","import_promises","import_node_path","import_node_path","rootPkg"]}
package/dist/index.js CHANGED
@@ -40,6 +40,7 @@ function aggregateStacks(packages) {
40
40
  }
41
41
  const styling = packages.find((p) => p.stack.styling)?.stack.styling;
42
42
  const backend = packages.find((p) => p.stack.backend)?.stack.backend;
43
+ const orm = packages.find((p) => p.stack.orm)?.stack.orm;
43
44
  const linter = packages.find((p) => p.stack.linter)?.stack.linter;
44
45
  const formatter = packages.find((p) => p.stack.formatter)?.stack.formatter;
45
46
  const testRunner = packages.find((p) => p.stack.testRunner)?.stack.testRunner;
@@ -50,6 +51,7 @@ function aggregateStacks(packages) {
50
51
  libraries: [...libraryMap.values()],
51
52
  styling,
52
53
  backend,
54
+ orm,
53
55
  linter,
54
56
  formatter,
55
57
  testRunner
@@ -476,44 +478,28 @@ async function detectImportAlias(projectPath) {
476
478
  import { access } from "fs/promises";
477
479
  import { join as join5 } from "path";
478
480
 
479
- // src/utils/read-package-json.ts
480
- import { readFile as readFile3 } from "fs/promises";
481
- import { join as join4 } from "path";
482
- async function readPackageJson(projectPath) {
483
- try {
484
- const raw = await readFile3(join4(projectPath, "package.json"), "utf-8");
485
- return JSON.parse(raw);
486
- } catch {
487
- return null;
488
- }
489
- }
490
-
491
- // src/detect-stack.ts
492
- function extractMajorVersion(range) {
493
- const match = range.match(/(\d+)/);
494
- return match?.[1];
495
- }
496
- async function fileExists(filePath) {
497
- try {
498
- await access(filePath);
499
- return true;
500
- } catch {
501
- return false;
502
- }
503
- }
481
+ // src/stack-mappings.ts
504
482
  var FRAMEWORK_MAPPINGS = [
505
483
  { dep: "next", name: "nextjs" },
506
484
  { dep: "expo", name: "expo" },
507
- { dep: "react-native", name: "react-native", excludeDep: "expo" },
485
+ { dep: "react-native", name: "react-native", excludeDeps: ["expo"] },
508
486
  { dep: "@angular/core", name: "angular" },
509
487
  { dep: "@sveltejs/kit", name: "sveltekit" },
510
- { dep: "svelte", name: "svelte" },
488
+ { dep: "svelte", name: "svelte", excludeDeps: ["@sveltejs/kit", "astro"] },
511
489
  { dep: "astro", name: "astro" },
512
490
  { dep: "@remix-run/react", name: "remix" },
513
491
  { dep: "nuxt", name: "nuxt" },
514
- { dep: "vue", name: "vue", excludeDep: "nuxt" },
492
+ { dep: "vue", name: "vue", excludeDeps: ["nuxt"] },
515
493
  { dep: "gatsby", name: "gatsby" },
516
- { dep: "react", name: "react", excludeDep: "next" }
494
+ { dep: "solid-js", name: "solidjs" },
495
+ { dep: "@builder.io/qwik", name: "qwik" },
496
+ { dep: "electron", name: "electron" },
497
+ { dep: "@tauri-apps/api", name: "tauri" },
498
+ {
499
+ dep: "react",
500
+ name: "react",
501
+ excludeDeps: ["next", "@remix-run/react", "gatsby", "expo"]
502
+ }
517
503
  ];
518
504
  var BACKEND_MAPPINGS = [
519
505
  { dep: "@nestjs/core", name: "nestjs" },
@@ -523,20 +509,78 @@ var BACKEND_MAPPINGS = [
523
509
  { dep: "hono", name: "hono" },
524
510
  { dep: "@supabase/supabase-js", name: "supabase" },
525
511
  { dep: "firebase", name: "firebase" },
512
+ { dep: "convex", name: "convex" }
513
+ ];
514
+ var ORM_MAPPINGS = [
526
515
  { dep: "@prisma/client", name: "prisma" },
527
516
  { dep: "prisma", name: "prisma" },
528
- { dep: "drizzle-orm", name: "drizzle" }
517
+ { dep: "drizzle-orm", name: "drizzle" },
518
+ { dep: "typeorm", name: "typeorm" },
519
+ { dep: "sequelize", name: "sequelize" },
520
+ { dep: "mongoose", name: "mongoose" },
521
+ { dep: "kysely", name: "kysely" },
522
+ { dep: "@mikro-orm/core", name: "mikro-orm" }
529
523
  ];
530
524
  var STYLING_MAPPINGS = [
531
525
  { dep: "tailwindcss", name: "tailwindcss" },
532
526
  { dep: "styled-components", name: "styled-components" },
533
527
  { dep: "@emotion/react", name: "emotion" },
534
- { dep: "sass", name: "sass" }
528
+ { dep: "sass", name: "sass" },
529
+ { dep: "@vanilla-extract/css", name: "vanilla-extract" },
530
+ { dep: "unocss", name: "unocss" },
531
+ { dep: "@pandacss/dev", name: "panda-css" },
532
+ { dep: "nativewind", name: "nativewind" }
535
533
  ];
536
534
  var LIBRARY_MAPPINGS = [
535
+ // Validation
537
536
  { deps: ["zod"], name: "zod" },
538
- { deps: ["@trpc/server", "@trpc/client"], name: "trpc" },
539
- { deps: ["@tanstack/react-query"], name: "react-query" }
537
+ // API
538
+ { deps: ["@trpc/server"], name: "trpc" },
539
+ { deps: ["@tanstack/react-query"], name: "react-query" },
540
+ { deps: ["@apollo/client"], name: "apollo" },
541
+ { deps: ["urql"], name: "urql" },
542
+ { deps: ["graphql"], name: "graphql" },
543
+ // State management
544
+ { deps: ["@reduxjs/toolkit"], name: "redux-toolkit" },
545
+ { deps: ["zustand"], name: "zustand" },
546
+ { deps: ["jotai"], name: "jotai" },
547
+ { deps: ["recoil"], name: "recoil" },
548
+ { deps: ["mobx"], name: "mobx" },
549
+ { deps: ["xstate"], name: "xstate" },
550
+ { deps: ["valtio"], name: "valtio" },
551
+ // Forms
552
+ { deps: ["react-hook-form"], name: "react-hook-form" },
553
+ { deps: ["formik"], name: "formik" },
554
+ // HTTP
555
+ { deps: ["axios"], name: "axios" },
556
+ // Auth
557
+ { deps: ["next-auth"], name: "next-auth" },
558
+ { deps: ["@auth/core"], name: "auth-js" },
559
+ { deps: ["@clerk/nextjs"], name: "clerk" },
560
+ { deps: ["lucia"], name: "lucia" },
561
+ // Dates
562
+ { deps: ["date-fns"], name: "date-fns" },
563
+ { deps: ["dayjs"], name: "dayjs" },
564
+ { deps: ["luxon"], name: "luxon" },
565
+ // i18n
566
+ { deps: ["i18next"], name: "i18next" },
567
+ { deps: ["next-i18next"], name: "next-i18next" },
568
+ // Payments
569
+ { deps: ["stripe"], name: "stripe" },
570
+ // Realtime
571
+ { deps: ["socket.io"], name: "socket.io" },
572
+ // Testing utilities
573
+ { deps: ["@testing-library/react"], name: "testing-library" },
574
+ { deps: ["msw"], name: "msw" },
575
+ { deps: ["storybook", "@storybook/react"], name: "storybook" },
576
+ // Bundlers
577
+ { deps: ["vite"], name: "vite" },
578
+ { deps: ["webpack"], name: "webpack" },
579
+ { deps: ["esbuild"], name: "esbuild" },
580
+ { deps: ["@rspack/core"], name: "rspack" },
581
+ // Monorepo
582
+ { deps: ["nx"], name: "nx" },
583
+ { deps: ["lerna"], name: "lerna" }
540
584
  ];
541
585
  var LOCK_FILE_MAP = [
542
586
  { file: "pnpm-lock.yaml", name: "pnpm" },
@@ -544,6 +588,32 @@ var LOCK_FILE_MAP = [
544
588
  { file: "bun.lockb", name: "bun" },
545
589
  { file: "package-lock.json", name: "npm" }
546
590
  ];
591
+
592
+ // src/utils/read-package-json.ts
593
+ import { readFile as readFile3 } from "fs/promises";
594
+ import { join as join4 } from "path";
595
+ async function readPackageJson(projectPath) {
596
+ try {
597
+ const raw = await readFile3(join4(projectPath, "package.json"), "utf-8");
598
+ return JSON.parse(raw);
599
+ } catch {
600
+ return null;
601
+ }
602
+ }
603
+
604
+ // src/detect-stack.ts
605
+ function extractMajorVersion(range) {
606
+ const match = range.match(/(\d+)/);
607
+ return match?.[1];
608
+ }
609
+ async function fileExists(filePath) {
610
+ try {
611
+ await access(filePath);
612
+ return true;
613
+ } catch {
614
+ return false;
615
+ }
616
+ }
547
617
  async function detectStack(projectPath, additionalDeps) {
548
618
  const pkg = await readPackageJson(projectPath);
549
619
  const allDeps = {
@@ -555,6 +625,7 @@ async function detectStack(projectPath, additionalDeps) {
555
625
  const language = await detectLanguage(projectPath, allDeps);
556
626
  const styling = detectFirst(allDeps, STYLING_MAPPINGS);
557
627
  const backend = detectFirst(allDeps, BACKEND_MAPPINGS);
628
+ const orm = detectFirst(allDeps, ORM_MAPPINGS);
558
629
  const packageManager = await detectPackageManager(projectPath);
559
630
  const linter = detectLinter(allDeps);
560
631
  const formatter = detectFormatter(allDeps);
@@ -565,6 +636,7 @@ async function detectStack(projectPath, additionalDeps) {
565
636
  language,
566
637
  ...styling && { styling },
567
638
  ...backend && { backend },
639
+ ...orm && { orm },
568
640
  packageManager,
569
641
  ...linter && { linter },
570
642
  ...formatter && { formatter },
@@ -575,7 +647,7 @@ async function detectStack(projectPath, additionalDeps) {
575
647
  function detectFramework(allDeps) {
576
648
  for (const mapping of FRAMEWORK_MAPPINGS) {
577
649
  if (!(mapping.dep in allDeps)) continue;
578
- if (mapping.excludeDep && mapping.excludeDep in allDeps) continue;
650
+ if (mapping.excludeDeps?.some((dep) => dep in allDeps)) continue;
579
651
  return {
580
652
  name: mapping.name,
581
653
  version: extractMajorVersion(allDeps[mapping.dep])
@@ -953,7 +1025,7 @@ async function scan(projectPath, _options) {
953
1025
  }
954
1026
 
955
1027
  // src/index.ts
956
- var VERSION = "0.2.3";
1028
+ var VERSION = "0.3.1";
957
1029
  export {
958
1030
  VERSION,
959
1031
  aggregateConventions,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/aggregate.ts","../src/compute-statistics.ts","../src/utils/walk-directory.ts","../src/detect-conventions.ts","../src/utils/classify-filename.ts","../src/detect-stack.ts","../src/utils/read-package-json.ts","../src/detect-structure.ts","../src/utils/classify-directory.ts","../src/detect-workspace.ts","../src/scan.ts","../src/scan-package.ts","../src/index.ts"],"sourcesContent":["import {\n type CodebaseStatistics,\n confidenceFromConsistency,\n type DetectedConvention,\n type DetectedStack,\n type DetectedStructure,\n type PackageScanResult,\n} from '@viberails/types';\n\n/** Framework priority order — higher-priority frameworks become the primary. */\nconst FRAMEWORK_PRIORITY = [\n 'nextjs',\n 'sveltekit',\n 'astro',\n 'expo',\n 'react-native',\n 'svelte',\n 'vue',\n 'react',\n];\n\n/**\n * Combines per-package stacks into a single aggregate stack.\n *\n * TypeScript wins over JavaScript if any package uses it.\n * The highest-priority framework becomes the primary; others go into libraries.\n */\nexport function aggregateStacks(packages: PackageScanResult[]): DetectedStack {\n if (packages.length === 1) return packages[0].stack;\n\n const tsPackage = packages.find((p) => p.stack.language.name === 'typescript');\n const language = tsPackage ? tsPackage.stack.language : packages[0].stack.language;\n\n const packageManager = packages[0].stack.packageManager;\n\n const frameworkPackages = packages.filter((p) => p.stack.framework);\n let framework: (typeof packages)[0]['stack']['framework'];\n if (frameworkPackages.length > 0) {\n frameworkPackages.sort((a, b) => {\n const aIdx = FRAMEWORK_PRIORITY.indexOf(a.stack.framework?.name ?? '');\n const bIdx = FRAMEWORK_PRIORITY.indexOf(b.stack.framework?.name ?? '');\n return (aIdx === -1 ? Infinity : aIdx) - (bIdx === -1 ? Infinity : bIdx);\n });\n framework = frameworkPackages[0].stack.framework;\n }\n\n const libraryMap = new Map<string, { name: string; version?: string }>();\n\n for (const pkg of packages) {\n if (pkg.stack.framework && pkg.stack.framework.name !== framework?.name) {\n libraryMap.set(pkg.stack.framework.name, pkg.stack.framework);\n }\n for (const lib of pkg.stack.libraries) {\n if (!libraryMap.has(lib.name)) {\n libraryMap.set(lib.name, lib);\n }\n }\n }\n\n const styling = packages.find((p) => p.stack.styling)?.stack.styling;\n const backend = packages.find((p) => p.stack.backend)?.stack.backend;\n const linter = packages.find((p) => p.stack.linter)?.stack.linter;\n const formatter = packages.find((p) => p.stack.formatter)?.stack.formatter;\n const testRunner = packages.find((p) => p.stack.testRunner)?.stack.testRunner;\n\n return {\n language,\n packageManager,\n framework,\n libraries: [...libraryMap.values()],\n styling,\n backend,\n linter,\n formatter,\n testRunner,\n };\n}\n\n/**\n * Combines per-package structures into a single aggregate structure.\n *\n * Directory paths are prefixed with the package's relativePath.\n */\nexport function aggregateStructures(packages: PackageScanResult[]): DetectedStructure {\n if (packages.length === 1) return packages[0].structure;\n\n const srcDir = packages.some((p) => p.structure.srcDir) ? 'src' : undefined;\n\n const directories = packages.flatMap((pkg) =>\n pkg.structure.directories.map((dir) => ({\n ...dir,\n path: pkg.relativePath ? `${pkg.relativePath}/${dir.path}` : dir.path,\n })),\n );\n\n const testPatterns = packages\n .map((p) => p.structure.testPattern)\n .filter((t): t is DetectedConvention<string> => t !== undefined);\n\n let testPattern: DetectedConvention<string> | undefined;\n if (testPatterns.length > 0) {\n const counts = new Map<string, { count: number; pattern: DetectedConvention<string> }>();\n for (const tp of testPatterns) {\n const existing = counts.get(tp.value);\n if (existing) {\n existing.count++;\n } else {\n counts.set(tp.value, { count: 1, pattern: tp });\n }\n }\n let best = { count: 0, pattern: testPatterns[0] };\n for (const entry of counts.values()) {\n if (entry.count > best.count) best = entry;\n }\n testPattern = best.pattern;\n }\n\n return { srcDir, directories, testPattern };\n}\n\n/**\n * Combines per-package conventions into aggregate conventions.\n *\n * When all packages agree on a convention, reports it with averaged consistency.\n * When packages disagree, scales consistency by agreement ratio.\n * Omits conventions present in fewer than half of packages.\n */\nexport function aggregateConventions(\n packages: PackageScanResult[],\n): Record<string, DetectedConvention> {\n if (packages.length === 1) return packages[0].conventions;\n\n const allKeys = new Set<string>();\n for (const pkg of packages) {\n for (const key of Object.keys(pkg.conventions)) {\n allKeys.add(key);\n }\n }\n\n const result: Record<string, DetectedConvention> = {};\n\n for (const key of allKeys) {\n const entries = packages\n .map((p) => p.conventions[key])\n .filter((c): c is DetectedConvention => c !== undefined);\n\n if (entries.length < packages.length / 2) continue;\n\n const valueCounts = new Map<\n string,\n { count: number; totalConsistency: number; totalSamples: number }\n >();\n for (const entry of entries) {\n const existing = valueCounts.get(entry.value);\n if (existing) {\n existing.count++;\n existing.totalConsistency += entry.consistency;\n existing.totalSamples += entry.sampleSize;\n } else {\n valueCounts.set(entry.value, {\n count: 1,\n totalConsistency: entry.consistency,\n totalSamples: entry.sampleSize,\n });\n }\n }\n\n let majorityValue = '';\n let majorityData = { count: 0, totalConsistency: 0, totalSamples: 0 };\n for (const [value, data] of valueCounts) {\n if (data.count > majorityData.count) {\n majorityValue = value;\n majorityData = data;\n }\n }\n\n const agreement = majorityData.count / entries.length;\n const avgConsistency = majorityData.totalConsistency / majorityData.count;\n const consistency = Math.round(avgConsistency * agreement);\n\n result[key] = {\n value: majorityValue,\n confidence: confidenceFromConsistency(consistency),\n sampleSize: majorityData.totalSamples,\n consistency,\n };\n }\n\n return result;\n}\n\n/**\n * Combines per-package statistics into aggregate statistics.\n *\n * Sums totals, recomputes averages, merges largest files with path prefixing.\n */\nexport function aggregateStatistics(packages: PackageScanResult[]): CodebaseStatistics {\n if (packages.length === 1) return packages[0].statistics;\n\n const totalFiles = packages.reduce((sum, p) => sum + p.statistics.totalFiles, 0);\n const totalLines = packages.reduce((sum, p) => sum + p.statistics.totalLines, 0);\n const averageFileLines = totalFiles > 0 ? Math.round(totalLines / totalFiles) : 0;\n\n const largestFiles = packages\n .flatMap((pkg) =>\n pkg.statistics.largestFiles.map((f) => ({\n path: pkg.relativePath ? `${pkg.relativePath}/${f.path}` : f.path,\n lines: f.lines,\n })),\n )\n .sort((a, b) => b.lines - a.lines)\n .slice(0, 5);\n\n const filesByExtension: Record<string, number> = {};\n for (const pkg of packages) {\n for (const [ext, count] of Object.entries(pkg.statistics.filesByExtension)) {\n filesByExtension[ext] = (filesByExtension[ext] ?? 0) + count;\n }\n }\n\n return { totalFiles, totalLines, averageFileLines, largestFiles, filesByExtension };\n}\n","import { readdir, readFile } from 'node:fs/promises';\nimport { extname, join } from 'node:path';\nimport type { CodebaseStatistics, FileStatistic } from '@viberails/types';\nimport type { WalkedDirectory } from './utils/walk-directory.js';\nimport { SOURCE_EXTENSIONS, walkDirectory } from './utils/walk-directory.js';\n\n/**\n * Counts lines in a file by reading its contents.\n *\n * @param filePath - Absolute path to the file.\n * @returns Number of lines, or 0 if the file can't be read.\n */\nasync function countLines(filePath: string): Promise<number> {\n try {\n const content = await readFile(filePath, 'utf-8');\n if (content.length === 0) return 0;\n let count = 0;\n for (let i = 0; i < content.length; i++) {\n if (content.charCodeAt(i) === 10) count++;\n }\n // A file with no trailing newline has one more line than newline count\n if (content.charCodeAt(content.length - 1) !== 10) count++;\n return count;\n } catch {\n return 0;\n }\n}\n\n/**\n * Collects root-level source file names from the project directory.\n *\n * @param projectPath - Absolute path to the project root.\n * @returns Array of source file names in the root directory.\n */\nasync function getRootSourceFiles(projectPath: string): Promise<string[]> {\n try {\n const entries = await readdir(projectPath, { withFileTypes: true });\n const sourceFiles: string[] = [];\n for (const entry of entries) {\n if (entry.isFile()) {\n const ext = extname(entry.name);\n if (SOURCE_EXTENSIONS.has(ext)) {\n sourceFiles.push(entry.name);\n }\n }\n }\n return sourceFiles;\n } catch {\n return [];\n }\n}\n\n/**\n * Computes quantitative statistics about a project's source files.\n *\n * Reads each source file and produces aggregate metrics including\n * file counts, line counts, and extension breakdown.\n *\n * @param projectPath - Absolute path to the project root.\n * @param dirs - Pre-walked directory list. If not provided, walks the directory tree.\n * @returns Statistics about the codebase.\n */\nexport async function computeStatistics(\n projectPath: string,\n dirs?: WalkedDirectory[],\n): Promise<CodebaseStatistics> {\n const directories = dirs ?? (await walkDirectory(projectPath));\n const rootFiles = await getRootSourceFiles(projectPath);\n\n // Collect all file paths and extensions\n const filesToProcess: Array<{ relativePath: string; absolutePath: string; ext: string }> = [];\n\n // Root-level source files\n for (const name of rootFiles) {\n filesToProcess.push({\n relativePath: name,\n absolutePath: join(projectPath, name),\n ext: extname(name),\n });\n }\n\n // Files from subdirectories\n for (const dir of directories) {\n for (const name of dir.sourceFileNames) {\n filesToProcess.push({\n relativePath: `${dir.relativePath}/${name}`,\n absolutePath: join(dir.absolutePath, name),\n ext: extname(name),\n });\n }\n }\n\n const totalFiles = filesToProcess.length;\n\n if (totalFiles === 0) {\n return {\n totalFiles: 0,\n totalLines: 0,\n averageFileLines: 0,\n largestFiles: [],\n filesByExtension: {},\n };\n }\n\n // Count lines for all files concurrently\n const lineResults = await Promise.all(\n filesToProcess.map(async (file) => ({\n path: file.relativePath,\n lines: await countLines(file.absolutePath),\n ext: file.ext,\n })),\n );\n\n let totalLines = 0;\n const filesByExtension: Record<string, number> = {};\n const allFiles: FileStatistic[] = [];\n\n for (const result of lineResults) {\n totalLines += result.lines;\n filesByExtension[result.ext] = (filesByExtension[result.ext] ?? 0) + 1;\n allFiles.push({ path: result.path, lines: result.lines });\n }\n\n // Sort descending by lines, take top 5\n allFiles.sort((a, b) => b.lines - a.lines);\n const largestFiles = allFiles.slice(0, 5);\n\n return {\n totalFiles,\n totalLines,\n averageFileLines: Math.round(totalLines / totalFiles),\n largestFiles,\n filesByExtension,\n };\n}\n","import { readdir } from 'node:fs/promises';\nimport { join, relative } from 'node:path';\n\n/** Directories to always skip during scanning. */\nconst IGNORED_DIRS = new Set([\n 'node_modules',\n '.git',\n 'dist',\n 'build',\n '.next',\n '.nuxt',\n '.viberails',\n 'coverage',\n '.turbo',\n '.cache',\n '.output',\n '.expo',\n 'android',\n 'ios',\n 'Pods',\n '.gradle',\n]);\n\n/** Source file extensions to count. */\nexport const SOURCE_EXTENSIONS = new Set([\n '.ts',\n '.tsx',\n '.js',\n '.jsx',\n '.mjs',\n '.cjs',\n '.vue',\n '.svelte',\n '.astro',\n]);\n\nexport interface WalkedDirectory {\n /** Path relative to project root, using forward slashes. */\n relativePath: string;\n /** Absolute path. */\n absolutePath: string;\n /** Number of source files directly in this directory. */\n sourceFileCount: number;\n /** Names of source files in this directory. */\n sourceFileNames: string[];\n /** Depth relative to the project root (1 = direct children). */\n depth: number;\n}\n\n/**\n * Walks a project directory tree using BFS, collecting directory info.\n *\n * @param projectPath - Absolute path to the project root.\n * @param maxDepth - Maximum directory depth to traverse (default 4).\n * @returns Flat list of all visited directories (excluding root itself).\n */\nexport async function walkDirectory(\n projectPath: string,\n maxDepth: number = 4,\n): Promise<WalkedDirectory[]> {\n const results: WalkedDirectory[] = [];\n const queue: Array<{ absolutePath: string; depth: number }> = [];\n\n try {\n const rootEntries = await readdir(projectPath, { withFileTypes: true });\n for (const entry of rootEntries) {\n if (entry.isDirectory() && !entry.isSymbolicLink() && !IGNORED_DIRS.has(entry.name)) {\n queue.push({ absolutePath: join(projectPath, entry.name), depth: 1 });\n }\n }\n } catch {\n return results;\n }\n\n while (queue.length > 0) {\n const item = queue.shift();\n if (!item) break;\n const { absolutePath, depth } = item;\n const sourceFileNames: string[] = [];\n\n try {\n const entries = await readdir(absolutePath, { withFileTypes: true });\n\n for (const entry of entries) {\n if (entry.isSymbolicLink()) continue;\n\n if (entry.isDirectory() && depth < maxDepth && !IGNORED_DIRS.has(entry.name)) {\n queue.push({ absolutePath: join(absolutePath, entry.name), depth: depth + 1 });\n } else if (entry.isFile()) {\n const dotIndex = entry.name.lastIndexOf('.');\n if (dotIndex > 0) {\n const ext = entry.name.substring(dotIndex);\n if (SOURCE_EXTENSIONS.has(ext)) {\n sourceFileNames.push(entry.name);\n }\n }\n }\n }\n } catch {\n continue;\n }\n\n const rel = relative(projectPath, absolutePath).split('\\\\').join('/');\n results.push({\n relativePath: rel,\n absolutePath,\n sourceFileCount: sourceFileNames.length,\n sourceFileNames,\n depth,\n });\n }\n\n return results;\n}\n","import { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { DetectedConvention, DetectedStructure } from '@viberails/types';\nimport { confidenceFromConsistency } from '@viberails/types';\nimport { classifyFilename } from './utils/classify-filename.js';\nimport type { WalkedDirectory } from './utils/walk-directory.js';\nimport { walkDirectory } from './utils/walk-directory.js';\n\n/**\n * Detects coding conventions used in a project by analyzing file names\n * and configuration files.\n *\n * @param projectPath - Absolute path to the project root directory.\n * @param structure - Previously detected directory structure, used to identify\n * directories by role.\n * @param dirs - Pre-walked directory list. If not provided, walks the directory tree.\n * @returns A record of detected conventions keyed by convention name.\n * Only statistical conventions with sampleSize >= 3 are included.\n */\nexport async function detectConventions(\n projectPath: string,\n structure: DetectedStructure,\n dirs?: WalkedDirectory[],\n): Promise<Record<string, DetectedConvention>> {\n if (!dirs) {\n dirs = await walkDirectory(projectPath, 4);\n }\n\n const result: Record<string, DetectedConvention> = {};\n\n const fileNaming = detectFileNaming(dirs);\n if (fileNaming) result.fileNaming = fileNaming;\n\n const componentNaming = detectComponentNaming(dirs, structure);\n if (componentNaming) result.componentNaming = componentNaming;\n\n const hookNaming = detectHookNaming(dirs, structure);\n if (hookNaming) result.hookNaming = hookNaming;\n\n // importAlias is binary (present or not) — bypasses sampleSize threshold\n const importAlias = await detectImportAlias(projectPath);\n if (importAlias) result.importAlias = importAlias;\n\n return result;\n}\n\n/** Strips the file extension, handling multi-dot names like foo.test.ts. */\nfunction stripExtension(filename: string): string {\n const firstDot = filename.indexOf('.');\n return firstDot > 0 ? filename.substring(0, firstDot) : filename;\n}\n\n/**\n * Detects the dominant file naming convention across all directories\n * with 3+ source files. Only returns conventions with consistency >= 70%\n * (medium or high confidence).\n */\nfunction detectFileNaming(dirs: WalkedDirectory[]): DetectedConvention | undefined {\n const conventionCounts = new Map<string, number>();\n let total = 0;\n\n for (const dir of dirs) {\n if (dir.sourceFileCount < 3) continue;\n\n for (const filename of dir.sourceFileNames) {\n if (filename.includes('.test.') || filename.includes('.spec.')) continue;\n const bare = stripExtension(filename);\n if (bare === 'index') continue;\n\n const convention = classifyFilename(bare);\n total++;\n if (convention !== 'unknown') {\n conventionCounts.set(convention, (conventionCounts.get(convention) ?? 0) + 1);\n }\n }\n }\n\n if (total < 3) return undefined;\n\n const sorted = [...conventionCounts.entries()].sort((a, b) => b[1] - a[1]);\n if (sorted.length === 0) return undefined;\n\n const [dominantConvention, dominantCount] = sorted[0];\n const consistency = Math.round((dominantCount / total) * 100);\n const confidence = confidenceFromConsistency(consistency);\n\n if (confidence === 'low') return undefined;\n\n return {\n value: dominantConvention,\n confidence,\n sampleSize: total,\n consistency,\n };\n}\n\n/**\n * Detects component naming convention by checking if .tsx files in\n * component directories use PascalCase filenames.\n */\nfunction detectComponentNaming(\n dirs: WalkedDirectory[],\n structure: DetectedStructure,\n): DetectedConvention | undefined {\n const componentPaths = new Set(\n structure.directories.filter((d) => d.role === 'components').map((d) => d.path),\n );\n\n const tsxFiles: string[] = [];\n for (const dir of dirs) {\n if (!componentPaths.has(dir.relativePath)) continue;\n for (const f of dir.sourceFileNames) {\n if (f.endsWith('.tsx')) tsxFiles.push(f);\n }\n }\n\n if (tsxFiles.length < 3) return undefined;\n\n const pascalCount = tsxFiles.filter((f) => /^[A-Z]/.test(stripExtension(f))).length;\n const consistency = Math.round((pascalCount / tsxFiles.length) * 100);\n const dominantValue = pascalCount >= tsxFiles.length / 2 ? 'PascalCase' : 'camelCase';\n\n return {\n value: dominantValue,\n confidence: confidenceFromConsistency(consistency),\n sampleSize: tsxFiles.length,\n consistency,\n };\n}\n\n/**\n * Detects hook naming convention by checking if hook files use\n * kebab-case (use-*) or camelCase (useXxx) prefix.\n */\nfunction detectHookNaming(\n dirs: WalkedDirectory[],\n structure: DetectedStructure,\n): DetectedConvention | undefined {\n const hookPaths = new Set(\n structure.directories.filter((d) => d.role === 'hooks').map((d) => d.path),\n );\n\n const hookFiles: string[] = [];\n for (const dir of dirs) {\n if (!hookPaths.has(dir.relativePath)) continue;\n for (const f of dir.sourceFileNames) {\n const bare = stripExtension(f);\n if (bare.startsWith('use-') || /^use[A-Z]/.test(bare)) {\n hookFiles.push(f);\n }\n }\n }\n\n if (hookFiles.length < 3) return undefined;\n\n let kebabCount = 0;\n let camelCount = 0;\n\n for (const filename of hookFiles) {\n const bare = stripExtension(filename);\n if (bare.startsWith('use-')) {\n kebabCount++;\n } else if (/^use[A-Z]/.test(bare)) {\n camelCount++;\n }\n }\n\n const total = hookFiles.length;\n const isDominantKebab = kebabCount >= camelCount;\n const dominantCount = isDominantKebab ? kebabCount : camelCount;\n const consistency = Math.round((dominantCount / total) * 100);\n\n return {\n value: isDominantKebab ? 'use-*' : 'useXxx',\n confidence: confidenceFromConsistency(consistency),\n sampleSize: total,\n consistency,\n };\n}\n\n/** Minimal shape for the tsconfig subset we read. */\ninterface TsConfigSubset {\n compilerOptions?: {\n paths?: Record<string, string[]>;\n };\n}\n\n/**\n * Detects import alias patterns from tsconfig.json or jsconfig.json paths configuration.\n * Checks tsconfig.json first, then falls back to jsconfig.json for JavaScript projects.\n */\nasync function detectImportAlias(projectPath: string): Promise<DetectedConvention | undefined> {\n for (const configFile of ['tsconfig.json', 'jsconfig.json']) {\n try {\n const raw = await readFile(join(projectPath, configFile), 'utf-8');\n const config = JSON.parse(raw) as TsConfigSubset;\n const paths = config.compilerOptions?.paths;\n if (!paths) continue;\n\n const aliases = Object.keys(paths);\n if (aliases.length === 0) continue;\n\n return {\n value: aliases.join(','),\n confidence: 'high',\n sampleSize: aliases.length,\n consistency: 100,\n };\n } catch {}\n }\n return undefined;\n}\n","/**\n * Naming convention types for filenames.\n */\nexport type FilenameConvention =\n | 'kebab-case'\n | 'camelCase'\n | 'PascalCase'\n | 'snake_case'\n | 'unknown';\n\nconst PATTERNS = [\n { convention: 'PascalCase', regex: /^[A-Z][a-zA-Z0-9]*$/ },\n { convention: 'camelCase', regex: /^[a-z][a-zA-Z0-9]*[A-Z][a-zA-Z0-9]*$/ },\n { convention: 'kebab-case', regex: /^[a-z][a-z0-9]*(-[a-z0-9]+)+$/ },\n { convention: 'snake_case', regex: /^[a-z][a-z0-9]*(_[a-z0-9]+)+$/ },\n] as const;\n\n/**\n * Classifies a filename (without extension) into a naming convention.\n *\n * Single lowercase words like 'utils' return 'unknown' because they are\n * ambiguous — they could be kebab-case, snake_case, or camelCase without\n * a disambiguating structural marker.\n *\n * @param filename - The bare filename with no extension (e.g. 'user-profile').\n * @returns The detected naming convention, or 'unknown' if ambiguous.\n */\nexport function classifyFilename(filename: string): FilenameConvention {\n for (const { convention, regex } of PATTERNS) {\n if (regex.test(filename)) return convention;\n }\n return 'unknown';\n}\n","import { access } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { DetectedStack, StackItem } from '@viberails/types';\nimport { readPackageJson } from './utils/read-package-json.js';\n\n/**\n * Extracts the major version number from a semver range string.\n *\n * @param range - A semver range such as `\"^15.0.3\"`, `\"~2.1.0\"`, or `\"3.x\"`.\n * @returns The major version string (e.g. `\"15\"`), or `undefined` if extraction fails.\n */\nexport function extractMajorVersion(range: string): string | undefined {\n const match = range.match(/(\\d+)/);\n return match?.[1];\n}\n\n/** Check whether a file exists at the given path. */\nasync function fileExists(filePath: string): Promise<boolean> {\n try {\n await access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Detection mapping tables\n// ---------------------------------------------------------------------------\n\ninterface FrameworkMapping {\n /** Package name to look for in dependencies. */\n dep: string;\n /** Name to use in the StackItem. */\n name: string;\n /** If set, the dep must NOT be present for this rule to match. */\n excludeDep?: string;\n}\n\nconst FRAMEWORK_MAPPINGS: FrameworkMapping[] = [\n { dep: 'next', name: 'nextjs' },\n { dep: 'expo', name: 'expo' },\n { dep: 'react-native', name: 'react-native', excludeDep: 'expo' },\n { dep: '@angular/core', name: 'angular' },\n { dep: '@sveltejs/kit', name: 'sveltekit' },\n { dep: 'svelte', name: 'svelte' },\n { dep: 'astro', name: 'astro' },\n { dep: '@remix-run/react', name: 'remix' },\n { dep: 'nuxt', name: 'nuxt' },\n { dep: 'vue', name: 'vue', excludeDep: 'nuxt' },\n { dep: 'gatsby', name: 'gatsby' },\n { dep: 'react', name: 'react', excludeDep: 'next' },\n];\n\nconst BACKEND_MAPPINGS: Array<{ dep: string; name: string }> = [\n { dep: '@nestjs/core', name: 'nestjs' },\n { dep: 'express', name: 'express' },\n { dep: 'fastify', name: 'fastify' },\n { dep: 'koa', name: 'koa' },\n { dep: 'hono', name: 'hono' },\n { dep: '@supabase/supabase-js', name: 'supabase' },\n { dep: 'firebase', name: 'firebase' },\n { dep: '@prisma/client', name: 'prisma' },\n { dep: 'prisma', name: 'prisma' },\n { dep: 'drizzle-orm', name: 'drizzle' },\n];\n\nconst STYLING_MAPPINGS: Array<{ dep: string; name: string }> = [\n { dep: 'tailwindcss', name: 'tailwindcss' },\n { dep: 'styled-components', name: 'styled-components' },\n { dep: '@emotion/react', name: 'emotion' },\n { dep: 'sass', name: 'sass' },\n];\n\nconst LIBRARY_MAPPINGS: Array<{ deps: string[]; name: string }> = [\n { deps: ['zod'], name: 'zod' },\n { deps: ['@trpc/server', '@trpc/client'], name: 'trpc' },\n { deps: ['@tanstack/react-query'], name: 'react-query' },\n];\n\nconst LOCK_FILE_MAP: Array<{ file: string; name: string }> = [\n { file: 'pnpm-lock.yaml', name: 'pnpm' },\n { file: 'yarn.lock', name: 'yarn' },\n { file: 'bun.lockb', name: 'bun' },\n { file: 'package-lock.json', name: 'npm' },\n];\n\n// ---------------------------------------------------------------------------\n// Main detection function\n// ---------------------------------------------------------------------------\n\n/**\n * Detects the technology stack of a project by reading its package.json\n * and checking for lock files and configuration files.\n *\n * When `additionalDeps` is provided, they are merged as a base layer\n * beneath the package's own deps. This allows monorepo root-level deps\n * (e.g. typescript, eslint) to be visible during per-package scanning.\n *\n * @param projectPath - Absolute path to the project root directory.\n * @param additionalDeps - Optional base dependencies merged under package deps.\n * @returns The detected technology stack.\n */\nexport async function detectStack(\n projectPath: string,\n additionalDeps?: Record<string, string>,\n): Promise<DetectedStack> {\n const pkg = await readPackageJson(projectPath);\n const allDeps: Record<string, string> = {\n ...additionalDeps,\n ...pkg?.dependencies,\n ...pkg?.devDependencies,\n };\n\n const framework = detectFramework(allDeps);\n const language = await detectLanguage(projectPath, allDeps);\n const styling = detectFirst(allDeps, STYLING_MAPPINGS);\n const backend = detectFirst(allDeps, BACKEND_MAPPINGS);\n const packageManager = await detectPackageManager(projectPath);\n const linter = detectLinter(allDeps);\n const formatter = detectFormatter(allDeps);\n const testRunner = detectTestRunner(allDeps);\n const libraries = detectLibraries(allDeps);\n\n return {\n ...(framework && { framework }),\n language,\n ...(styling && { styling }),\n ...(backend && { backend }),\n packageManager,\n ...(linter && { linter }),\n ...(formatter && { formatter }),\n ...(testRunner && { testRunner }),\n libraries,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Individual detectors\n// ---------------------------------------------------------------------------\n\nfunction detectFramework(allDeps: Record<string, string>): StackItem | undefined {\n for (const mapping of FRAMEWORK_MAPPINGS) {\n if (!(mapping.dep in allDeps)) continue;\n if (mapping.excludeDep && mapping.excludeDep in allDeps) continue;\n return {\n name: mapping.name,\n version: extractMajorVersion(allDeps[mapping.dep]),\n };\n }\n return undefined;\n}\n\nasync function detectLanguage(\n projectPath: string,\n allDeps: Record<string, string>,\n): Promise<StackItem> {\n if ('typescript' in allDeps) {\n return {\n name: 'typescript',\n version: extractMajorVersion(allDeps.typescript),\n };\n }\n if (await fileExists(join(projectPath, 'tsconfig.json'))) {\n return { name: 'typescript' };\n }\n return { name: 'javascript' };\n}\n\nfunction detectFirst(\n allDeps: Record<string, string>,\n mappings: Array<{ dep: string; name: string }>,\n): StackItem | undefined {\n for (const mapping of mappings) {\n if (mapping.dep in allDeps) {\n return {\n name: mapping.name,\n version: extractMajorVersion(allDeps[mapping.dep]),\n };\n }\n }\n return undefined;\n}\n\nasync function detectPackageManager(projectPath: string): Promise<StackItem> {\n for (const entry of LOCK_FILE_MAP) {\n if (await fileExists(join(projectPath, entry.file))) {\n return { name: entry.name };\n }\n }\n return { name: 'npm' };\n}\n\nfunction detectLinter(allDeps: Record<string, string>): StackItem | undefined {\n if ('eslint' in allDeps) {\n return { name: 'eslint', version: extractMajorVersion(allDeps.eslint) };\n }\n if ('@biomejs/biome' in allDeps) {\n return {\n name: 'biome',\n version: extractMajorVersion(allDeps['@biomejs/biome']),\n };\n }\n return undefined;\n}\n\nfunction detectFormatter(allDeps: Record<string, string>): StackItem | undefined {\n if ('prettier' in allDeps) {\n return { name: 'prettier', version: extractMajorVersion(allDeps.prettier) };\n }\n if ('@biomejs/biome' in allDeps) {\n return {\n name: 'biome',\n version: extractMajorVersion(allDeps['@biomejs/biome']),\n };\n }\n return undefined;\n}\n\nfunction detectTestRunner(allDeps: Record<string, string>): StackItem | undefined {\n if ('vitest' in allDeps) {\n return { name: 'vitest', version: extractMajorVersion(allDeps.vitest) };\n }\n if ('jest' in allDeps) {\n return { name: 'jest', version: extractMajorVersion(allDeps.jest) };\n }\n if ('@playwright/test' in allDeps) {\n return { name: 'playwright', version: extractMajorVersion(allDeps['@playwright/test']) };\n }\n if ('cypress' in allDeps) {\n return { name: 'cypress', version: extractMajorVersion(allDeps.cypress) };\n }\n if ('mocha' in allDeps) {\n return { name: 'mocha', version: extractMajorVersion(allDeps.mocha) };\n }\n return undefined;\n}\n\nfunction detectLibraries(allDeps: Record<string, string>): StackItem[] {\n const libs: StackItem[] = [];\n for (const mapping of LIBRARY_MAPPINGS) {\n const found = mapping.deps.find((dep) => dep in allDeps);\n if (found) {\n libs.push({\n name: mapping.name,\n version: extractMajorVersion(allDeps[found]),\n });\n }\n }\n return libs;\n}\n","import { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\n/**\n * Minimal interface for the fields we read from package.json.\n */\nexport interface PackageJson {\n name?: string;\n version?: string;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n}\n\n/**\n * Safely reads and parses a package.json file from the given directory.\n *\n * @param projectPath - Absolute path to the project root directory.\n * @returns Parsed package.json contents, or `null` if the file doesn't exist or is invalid JSON.\n */\nexport async function readPackageJson(projectPath: string): Promise<PackageJson | null> {\n try {\n const raw = await readFile(join(projectPath, 'package.json'), 'utf-8');\n return JSON.parse(raw) as PackageJson;\n } catch {\n return null;\n }\n}\n","import type { DetectedConvention, DetectedStructure } from '@viberails/types';\nimport { confidenceFromConsistency } from '@viberails/types';\nimport { classifyDirectory } from './utils/classify-directory.js';\nimport type { WalkedDirectory } from './utils/walk-directory.js';\nimport { walkDirectory } from './utils/walk-directory.js';\n\n/**\n * Detects the directory structure and organization of a project.\n *\n * Classifies directories by role, detects whether a src/ directory is in use,\n * and identifies test file patterns.\n *\n * @param projectPath - Absolute path to the project root directory.\n * @param dirs - Pre-walked directory list. If not provided, walks the directory tree.\n * @returns The detected directory structure.\n */\nexport async function detectStructure(\n projectPath: string,\n dirs?: WalkedDirectory[],\n): Promise<DetectedStructure> {\n if (!dirs) {\n dirs = await walkDirectory(projectPath, 4);\n }\n\n // Detect srcDir\n const hasSrcDir = dirs.some((d) => d.relativePath === 'src' || d.relativePath.startsWith('src/'));\n const srcDir = hasSrcDir ? 'src' : undefined;\n\n // Classify directories\n const directories = dirs.map((d) => classifyDirectory(d)).filter((d) => d !== null);\n\n // Detect test pattern\n const testPattern = detectTestPattern(dirs);\n\n return {\n ...(srcDir !== undefined && { srcDir }),\n directories,\n ...(testPattern !== undefined && { testPattern }),\n };\n}\n\n/**\n * Detects the dominant test file naming pattern from all source files.\n * Returns undefined if fewer than 3 test files are found.\n */\nfunction detectTestPattern(\n dirs: Array<{ sourceFileNames: string[] }>,\n): DetectedConvention<string> | undefined {\n const allFiles = dirs.flatMap((d) => d.sourceFileNames);\n const testFiles = allFiles.filter((f) => f.includes('.test.') || f.includes('.spec.'));\n\n if (testFiles.length < 3) return undefined;\n\n const dotTestCount = testFiles.filter((f) => f.includes('.test.')).length;\n const dotSpecCount = testFiles.filter((f) => f.includes('.spec.')).length;\n\n const isDotTest = dotTestCount >= dotSpecCount;\n const dominantSep = isDotTest ? '.test.' : '.spec.';\n const dominantCount = isDotTest ? dotTestCount : dotSpecCount;\n const consistency = Math.round((dominantCount / testFiles.length) * 100);\n\n // Find most common extension among dominant test files\n const extCounts = new Map<string, number>();\n for (const f of testFiles.filter((f) => f.includes(dominantSep))) {\n const ext = f.substring(f.lastIndexOf('.'));\n extCounts.set(ext, (extCounts.get(ext) ?? 0) + 1);\n }\n const topExt = [...extCounts.entries()].sort((a, b) => b[1] - a[1])[0]?.[0] ?? '.ts';\n\n const sep = isDotTest ? 'test' : 'spec';\n\n return {\n value: `*.${sep}${topExt}`,\n confidence: confidenceFromConsistency(consistency),\n sampleSize: testFiles.length,\n consistency,\n };\n}\n","import type { Confidence, DirectoryRole } from '@viberails/types';\nimport type { WalkedDirectory } from './walk-directory.js';\n\nexport interface ClassifiedDirectory {\n path: string;\n role: DirectoryRole;\n fileCount: number;\n confidence: Confidence;\n}\n\ninterface RolePattern {\n role: DirectoryRole;\n pathPatterns: string[];\n}\n\nconst ROLE_PATTERNS: RolePattern[] = [\n { role: 'pages', pathPatterns: ['src/app', 'src/pages', 'app', 'pages'] },\n { role: 'components', pathPatterns: ['src/components', 'components'] },\n { role: 'hooks', pathPatterns: ['src/hooks', 'hooks'] },\n {\n role: 'utils',\n pathPatterns: ['src/lib', 'src/utils', 'src/helpers', 'lib', 'utils', 'helpers'],\n },\n { role: 'types', pathPatterns: ['src/types', 'types', 'src/@types', '@types'] },\n { role: 'tests', pathPatterns: ['__tests__', 'tests', 'test', 'src/__tests__', 'src/tests'] },\n { role: 'styles', pathPatterns: ['src/styles', 'styles', 'src/css', 'css'] },\n { role: 'api', pathPatterns: ['src/api', 'api', 'src/app/api', 'app/api'] },\n { role: 'config', pathPatterns: ['config', 'src/config'] },\n];\n\n/**\n * Classifies a directory by its role in the project.\n *\n * @param dir - A walked directory entry.\n * @returns Classified directory info, or null if the directory should be skipped.\n */\nexport function classifyDirectory(dir: WalkedDirectory): ClassifiedDirectory | null {\n // Try name-based matching first\n const nameMatch = matchByName(dir.relativePath);\n if (nameMatch) {\n const confidence: Confidence = dir.sourceFileCount > 0 ? 'high' : 'low';\n return {\n path: dir.relativePath,\n role: nameMatch,\n fileCount: dir.sourceFileCount,\n confidence,\n };\n }\n\n // Skip directories with no source files if no name match\n if (dir.sourceFileCount === 0) return null;\n\n // Content-based heuristics\n const contentRole = inferFromContent(dir);\n if (contentRole) return contentRole;\n\n // Has source files but no classification\n return {\n path: dir.relativePath,\n role: 'unknown',\n fileCount: dir.sourceFileCount,\n confidence: 'low',\n };\n}\n\n/**\n * Matches a relative path against known role patterns.\n * Supports suffix matching so monorepo paths like `apps/web/lib`\n * match patterns like `lib`.\n * Returns the role if matched, or null.\n */\nfunction matchByName(relativePath: string): DirectoryRole | null {\n for (const { role, pathPatterns } of ROLE_PATTERNS) {\n for (const pattern of pathPatterns) {\n if (relativePath === pattern || relativePath.endsWith(`/${pattern}`)) {\n return role;\n }\n }\n }\n return null;\n}\n\n/**\n * Infers directory role from file contents/names.\n */\nfunction inferFromContent(dir: WalkedDirectory): ClassifiedDirectory | null {\n const { sourceFileNames, sourceFileCount } = dir;\n\n // Check for hook files (use-* kebab or useXxx camelCase prefix)\n // Require at least 2 matching files to avoid misclassifying directories\n // with a single hook utility (e.g. lib/ with one useXxx file)\n const hookFiles = sourceFileNames.filter((f) => {\n const name = f.split('.')[0];\n return name.startsWith('use-') || /^use[A-Z]/.test(name);\n });\n if (hookFiles.length >= 2 && hookFiles.length / sourceFileCount >= 0.5) {\n return {\n path: dir.relativePath,\n role: 'hooks',\n fileCount: sourceFileCount,\n confidence: hookFiles.length / sourceFileCount >= 0.9 ? 'high' : 'medium',\n };\n }\n\n // Check for test files — require at least 2 to avoid false positives\n const testFiles = sourceFileNames.filter((f) => f.includes('.test.') || f.includes('.spec.'));\n if (testFiles.length >= 2 && testFiles.length / sourceFileCount >= 0.5) {\n return {\n path: dir.relativePath,\n role: 'tests',\n fileCount: sourceFileCount,\n confidence: testFiles.length / sourceFileCount >= 0.9 ? 'high' : 'medium',\n };\n }\n\n return null;\n}\n","import { readdir, readFile } from 'node:fs/promises';\nimport { join, relative } from 'node:path';\nimport type { DetectedWorkspace, WorkspacePackage } from '@viberails/types';\nimport { readPackageJson } from './utils/read-package-json.js';\n\n/**\n * Detects workspace configuration for monorepo projects.\n *\n * Checks for `pnpm-workspace.yaml` first, then falls back to\n * `package.json` `workspaces` field. Returns `undefined` for\n * single-package projects.\n *\n * @param projectRoot - Absolute path to the project root.\n * @returns Detected workspace info, or `undefined` if not a monorepo.\n */\nexport async function detectWorkspace(projectRoot: string): Promise<DetectedWorkspace | undefined> {\n const patterns = await readWorkspacePatterns(projectRoot);\n if (!patterns || patterns.length === 0) return undefined;\n\n const packageDirs = await resolvePatterns(projectRoot, patterns);\n const packages = await resolvePackages(projectRoot, packageDirs);\n\n if (packages.length === 0) return undefined;\n\n // Resolve internal deps: check if any dependency name matches a workspace package\n const packageNames = new Set(packages.map((p) => p.name));\n for (const pkg of packages) {\n pkg.internalDeps = pkg.internalDeps.filter((dep) => packageNames.has(dep));\n }\n\n return { patterns, packages };\n}\n\n/**\n * Reads workspace glob patterns from pnpm-workspace.yaml or package.json.\n */\nasync function readWorkspacePatterns(projectRoot: string): Promise<string[] | undefined> {\n // Try pnpm-workspace.yaml first\n try {\n const yaml = await readFile(join(projectRoot, 'pnpm-workspace.yaml'), 'utf-8');\n return parsePnpmWorkspaceYaml(yaml);\n } catch {\n // Not found, try package.json\n }\n\n // Fall back to package.json workspaces field\n const pkg = await readPackageJson(projectRoot);\n if (!pkg) return undefined;\n\n const raw = pkg as Record<string, unknown>;\n const workspaces = raw.workspaces;\n\n if (Array.isArray(workspaces)) {\n return workspaces.filter((w): w is string => typeof w === 'string');\n }\n\n // Handle { packages: [...] } format\n if (workspaces && typeof workspaces === 'object' && 'packages' in workspaces) {\n const nested = (workspaces as { packages: unknown }).packages;\n if (Array.isArray(nested)) {\n return nested.filter((w): w is string => typeof w === 'string');\n }\n }\n\n return undefined;\n}\n\n/**\n * Parses workspace patterns from pnpm-workspace.yaml content.\n * Handles the common format: `packages:\\n - 'packages/*'`\n */\nfunction parsePnpmWorkspaceYaml(content: string): string[] {\n const patterns: string[] = [];\n let inPackages = false;\n\n for (const line of content.split('\\n')) {\n const trimmed = line.trim();\n\n if (trimmed === 'packages:') {\n inPackages = true;\n continue;\n }\n\n // Stop at next top-level key\n if (inPackages && trimmed.length > 0 && !trimmed.startsWith('-')) {\n break;\n }\n\n if (inPackages && trimmed.startsWith('-')) {\n // Extract the pattern, stripping quotes and leading dash\n const value = trimmed\n .slice(1)\n .trim()\n .replace(/^['\"]|['\"]$/g, '');\n if (value) patterns.push(value);\n }\n }\n\n return patterns;\n}\n\n/**\n * Resolves workspace glob patterns to actual directory paths.\n * Supports simple `*` wildcard matching (e.g. `packages/*`).\n */\nasync function resolvePatterns(projectRoot: string, patterns: string[]): Promise<string[]> {\n const dirs: string[] = [];\n\n for (const pattern of patterns) {\n if (pattern.endsWith('/*')) {\n // Simple wildcard: list children of the parent directory\n const parent = join(projectRoot, pattern.slice(0, -2));\n try {\n const entries = await readdir(parent, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory()) {\n dirs.push(join(parent, entry.name));\n }\n }\n } catch {\n // Parent directory doesn't exist, skip\n }\n } else {\n // Literal path\n dirs.push(join(projectRoot, pattern));\n }\n }\n\n return dirs;\n}\n\n/**\n * Resolves workspace directories to WorkspacePackage objects.\n * Skips directories without a valid package.json.\n */\nasync function resolvePackages(projectRoot: string, dirs: string[]): Promise<WorkspacePackage[]> {\n const packages: WorkspacePackage[] = [];\n\n for (const dir of dirs) {\n const pkg = await readPackageJson(dir);\n if (!pkg?.name) continue;\n\n // Collect all dependency names as potential internal deps\n const allDeps = [\n ...Object.keys(pkg.dependencies ?? {}),\n ...Object.keys(pkg.devDependencies ?? {}),\n ];\n\n packages.push({\n name: pkg.name,\n path: dir,\n relativePath: relative(projectRoot, dir),\n internalDeps: allDeps,\n });\n }\n\n return packages;\n}\n","import { stat } from 'node:fs/promises';\nimport { basename, resolve } from 'node:path';\nimport type { ScanResult } from '@viberails/types';\nimport {\n aggregateConventions,\n aggregateStacks,\n aggregateStatistics,\n aggregateStructures,\n} from './aggregate.js';\nimport { detectWorkspace } from './detect-workspace.js';\nimport { scanPackage } from './scan-package.js';\nimport { readPackageJson } from './utils/read-package-json.js';\n\n/**\n * Options for the scan function.\n */\nexport type ScanOptions = Record<string, never>;\n\n/**\n * Scans a project directory and returns a comprehensive analysis of its\n * stack, structure, conventions, and statistics.\n *\n * For monorepos, each workspace package is scanned independently and\n * results are aggregated. For single-package projects, the root is\n * scanned as a single package.\n *\n * @param projectPath - Absolute or relative path to the project root.\n * @param options - Optional scan configuration.\n * @returns Complete scan result for the project.\n * @throws If the project path does not exist or is not a directory.\n */\nexport async function scan(projectPath: string, _options?: ScanOptions): Promise<ScanResult> {\n const root = resolve(projectPath);\n\n // Validate that the path exists and is a directory\n try {\n const st = await stat(root);\n if (!st.isDirectory()) {\n throw new Error(`Project path is not a directory: ${root}`);\n }\n } catch (err) {\n if (err instanceof Error && err.message.startsWith('Project path is not')) {\n throw err;\n }\n throw new Error(`Project path does not exist: ${root}`);\n }\n\n const workspace = await detectWorkspace(root);\n\n if (workspace && workspace.packages.length > 0) {\n // Read root deps for sharing with workspace packages\n const rootPkg = await readPackageJson(root);\n const rootDeps: Record<string, string> = {\n ...rootPkg?.dependencies,\n ...rootPkg?.devDependencies,\n };\n\n // Scan each workspace package in parallel\n const packages = await Promise.all(\n workspace.packages.map((wp) => scanPackage(wp.path, wp.name, wp.relativePath, rootDeps)),\n );\n\n return {\n root,\n stack: aggregateStacks(packages),\n structure: aggregateStructures(packages),\n conventions: aggregateConventions(packages),\n statistics: aggregateStatistics(packages),\n workspace,\n packages,\n };\n }\n\n // Single-package project\n const rootPkg = await readPackageJson(root);\n const name = rootPkg?.name ?? basename(root);\n const pkg = await scanPackage(root, name, '');\n\n return {\n root,\n stack: pkg.stack,\n structure: pkg.structure,\n conventions: pkg.conventions,\n statistics: pkg.statistics,\n packages: [pkg],\n };\n}\n","import { resolve } from 'node:path';\nimport type { PackageScanResult } from '@viberails/types';\nimport { computeStatistics } from './compute-statistics.js';\nimport { detectConventions } from './detect-conventions.js';\nimport { detectStack } from './detect-stack.js';\nimport { detectStructure } from './detect-structure.js';\nimport { walkDirectory } from './utils/walk-directory.js';\n\n/**\n * Scans a single package directory and returns per-package scan results.\n *\n * Reuses all existing detector functions scoped to the package directory.\n * In monorepos, `rootDeps` provides root-level dependencies (e.g. typescript,\n * eslint) as a base layer — package-specific deps overlay on top.\n *\n * @param packagePath - Absolute path to the package directory.\n * @param name - Package name from package.json.\n * @param relativePath - Path relative to workspace root (empty string for single-package).\n * @param rootDeps - Optional root-level dependencies merged as a base layer.\n * @returns Per-package scan result.\n */\nexport async function scanPackage(\n packagePath: string,\n name: string,\n relativePath: string,\n rootDeps?: Record<string, string>,\n): Promise<PackageScanResult> {\n const root = resolve(packagePath);\n const dirs = await walkDirectory(root, 4);\n\n const [stack, structure, statistics] = await Promise.all([\n detectStack(root, rootDeps),\n detectStructure(root, dirs),\n computeStatistics(root, dirs),\n ]);\n\n const conventions = await detectConventions(root, structure, dirs);\n\n return {\n name,\n root,\n relativePath,\n stack,\n structure,\n conventions,\n statistics,\n };\n}\n","declare const __PACKAGE_VERSION__: string;\nexport const VERSION: string = __PACKAGE_VERSION__;\n\nexport {\n aggregateConventions,\n aggregateStacks,\n aggregateStatistics,\n aggregateStructures,\n} from './aggregate.js';\nexport { computeStatistics } from './compute-statistics.js';\nexport { detectConventions } from './detect-conventions.js';\nexport { detectStack, extractMajorVersion } from './detect-stack.js';\nexport { detectStructure } from './detect-structure.js';\nexport { detectWorkspace } from './detect-workspace.js';\nexport type { ScanOptions } from './scan.js';\nexport { scan } from './scan.js';\nexport { scanPackage } from './scan-package.js';\nexport type { PackageJson } from './utils/read-package-json.js';\nexport { readPackageJson } from './utils/read-package-json.js';\nexport type { WalkedDirectory } from './utils/walk-directory.js';\nexport { walkDirectory } from './utils/walk-directory.js';\n"],"mappings":"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","names":["readdir","join","readdir","join","readFile","join","confidenceFromConsistency","confidenceFromConsistency","readFile","join","join","readFile","join","join","confidenceFromConsistency","f","confidenceFromConsistency","readdir","readFile","join","relative","readFile","join","readdir","relative","resolve","resolve","rootPkg"]}
1
+ {"version":3,"sources":["../src/aggregate.ts","../src/compute-statistics.ts","../src/utils/walk-directory.ts","../src/detect-conventions.ts","../src/utils/classify-filename.ts","../src/detect-stack.ts","../src/stack-mappings.ts","../src/utils/read-package-json.ts","../src/detect-structure.ts","../src/utils/classify-directory.ts","../src/detect-workspace.ts","../src/scan.ts","../src/scan-package.ts","../src/index.ts"],"sourcesContent":["import {\n type CodebaseStatistics,\n confidenceFromConsistency,\n type DetectedConvention,\n type DetectedStack,\n type DetectedStructure,\n type PackageScanResult,\n} from '@viberails/types';\n\n/** Framework priority order — higher-priority frameworks become the primary. */\nconst FRAMEWORK_PRIORITY = [\n 'nextjs',\n 'sveltekit',\n 'astro',\n 'expo',\n 'react-native',\n 'svelte',\n 'vue',\n 'react',\n];\n\n/**\n * Combines per-package stacks into a single aggregate stack.\n *\n * TypeScript wins over JavaScript if any package uses it.\n * The highest-priority framework becomes the primary; others go into libraries.\n */\nexport function aggregateStacks(packages: PackageScanResult[]): DetectedStack {\n if (packages.length === 1) return packages[0].stack;\n\n const tsPackage = packages.find((p) => p.stack.language.name === 'typescript');\n const language = tsPackage ? tsPackage.stack.language : packages[0].stack.language;\n\n const packageManager = packages[0].stack.packageManager;\n\n const frameworkPackages = packages.filter((p) => p.stack.framework);\n let framework: (typeof packages)[0]['stack']['framework'];\n if (frameworkPackages.length > 0) {\n frameworkPackages.sort((a, b) => {\n const aIdx = FRAMEWORK_PRIORITY.indexOf(a.stack.framework?.name ?? '');\n const bIdx = FRAMEWORK_PRIORITY.indexOf(b.stack.framework?.name ?? '');\n return (aIdx === -1 ? Infinity : aIdx) - (bIdx === -1 ? Infinity : bIdx);\n });\n framework = frameworkPackages[0].stack.framework;\n }\n\n const libraryMap = new Map<string, { name: string; version?: string }>();\n\n for (const pkg of packages) {\n if (pkg.stack.framework && pkg.stack.framework.name !== framework?.name) {\n libraryMap.set(pkg.stack.framework.name, pkg.stack.framework);\n }\n for (const lib of pkg.stack.libraries) {\n if (!libraryMap.has(lib.name)) {\n libraryMap.set(lib.name, lib);\n }\n }\n }\n\n const styling = packages.find((p) => p.stack.styling)?.stack.styling;\n const backend = packages.find((p) => p.stack.backend)?.stack.backend;\n const orm = packages.find((p) => p.stack.orm)?.stack.orm;\n const linter = packages.find((p) => p.stack.linter)?.stack.linter;\n const formatter = packages.find((p) => p.stack.formatter)?.stack.formatter;\n const testRunner = packages.find((p) => p.stack.testRunner)?.stack.testRunner;\n\n return {\n language,\n packageManager,\n framework,\n libraries: [...libraryMap.values()],\n styling,\n backend,\n orm,\n linter,\n formatter,\n testRunner,\n };\n}\n\n/**\n * Combines per-package structures into a single aggregate structure.\n *\n * Directory paths are prefixed with the package's relativePath.\n */\nexport function aggregateStructures(packages: PackageScanResult[]): DetectedStructure {\n if (packages.length === 1) return packages[0].structure;\n\n const srcDir = packages.some((p) => p.structure.srcDir) ? 'src' : undefined;\n\n const directories = packages.flatMap((pkg) =>\n pkg.structure.directories.map((dir) => ({\n ...dir,\n path: pkg.relativePath ? `${pkg.relativePath}/${dir.path}` : dir.path,\n })),\n );\n\n const testPatterns = packages\n .map((p) => p.structure.testPattern)\n .filter((t): t is DetectedConvention<string> => t !== undefined);\n\n let testPattern: DetectedConvention<string> | undefined;\n if (testPatterns.length > 0) {\n const counts = new Map<string, { count: number; pattern: DetectedConvention<string> }>();\n for (const tp of testPatterns) {\n const existing = counts.get(tp.value);\n if (existing) {\n existing.count++;\n } else {\n counts.set(tp.value, { count: 1, pattern: tp });\n }\n }\n let best = { count: 0, pattern: testPatterns[0] };\n for (const entry of counts.values()) {\n if (entry.count > best.count) best = entry;\n }\n testPattern = best.pattern;\n }\n\n return { srcDir, directories, testPattern };\n}\n\n/**\n * Combines per-package conventions into aggregate conventions.\n *\n * When all packages agree on a convention, reports it with averaged consistency.\n * When packages disagree, scales consistency by agreement ratio.\n * Omits conventions present in fewer than half of packages.\n */\nexport function aggregateConventions(\n packages: PackageScanResult[],\n): Record<string, DetectedConvention> {\n if (packages.length === 1) return packages[0].conventions;\n\n const allKeys = new Set<string>();\n for (const pkg of packages) {\n for (const key of Object.keys(pkg.conventions)) {\n allKeys.add(key);\n }\n }\n\n const result: Record<string, DetectedConvention> = {};\n\n for (const key of allKeys) {\n const entries = packages\n .map((p) => p.conventions[key])\n .filter((c): c is DetectedConvention => c !== undefined);\n\n if (entries.length < packages.length / 2) continue;\n\n const valueCounts = new Map<\n string,\n { count: number; totalConsistency: number; totalSamples: number }\n >();\n for (const entry of entries) {\n const existing = valueCounts.get(entry.value);\n if (existing) {\n existing.count++;\n existing.totalConsistency += entry.consistency;\n existing.totalSamples += entry.sampleSize;\n } else {\n valueCounts.set(entry.value, {\n count: 1,\n totalConsistency: entry.consistency,\n totalSamples: entry.sampleSize,\n });\n }\n }\n\n let majorityValue = '';\n let majorityData = { count: 0, totalConsistency: 0, totalSamples: 0 };\n for (const [value, data] of valueCounts) {\n if (data.count > majorityData.count) {\n majorityValue = value;\n majorityData = data;\n }\n }\n\n const agreement = majorityData.count / entries.length;\n const avgConsistency = majorityData.totalConsistency / majorityData.count;\n const consistency = Math.round(avgConsistency * agreement);\n\n result[key] = {\n value: majorityValue,\n confidence: confidenceFromConsistency(consistency),\n sampleSize: majorityData.totalSamples,\n consistency,\n };\n }\n\n return result;\n}\n\n/**\n * Combines per-package statistics into aggregate statistics.\n *\n * Sums totals, recomputes averages, merges largest files with path prefixing.\n */\nexport function aggregateStatistics(packages: PackageScanResult[]): CodebaseStatistics {\n if (packages.length === 1) return packages[0].statistics;\n\n const totalFiles = packages.reduce((sum, p) => sum + p.statistics.totalFiles, 0);\n const totalLines = packages.reduce((sum, p) => sum + p.statistics.totalLines, 0);\n const averageFileLines = totalFiles > 0 ? Math.round(totalLines / totalFiles) : 0;\n\n const largestFiles = packages\n .flatMap((pkg) =>\n pkg.statistics.largestFiles.map((f) => ({\n path: pkg.relativePath ? `${pkg.relativePath}/${f.path}` : f.path,\n lines: f.lines,\n })),\n )\n .sort((a, b) => b.lines - a.lines)\n .slice(0, 5);\n\n const filesByExtension: Record<string, number> = {};\n for (const pkg of packages) {\n for (const [ext, count] of Object.entries(pkg.statistics.filesByExtension)) {\n filesByExtension[ext] = (filesByExtension[ext] ?? 0) + count;\n }\n }\n\n return { totalFiles, totalLines, averageFileLines, largestFiles, filesByExtension };\n}\n","import { readdir, readFile } from 'node:fs/promises';\nimport { extname, join } from 'node:path';\nimport type { CodebaseStatistics, FileStatistic } from '@viberails/types';\nimport type { WalkedDirectory } from './utils/walk-directory.js';\nimport { SOURCE_EXTENSIONS, walkDirectory } from './utils/walk-directory.js';\n\n/**\n * Counts lines in a file by reading its contents.\n *\n * @param filePath - Absolute path to the file.\n * @returns Number of lines, or 0 if the file can't be read.\n */\nasync function countLines(filePath: string): Promise<number> {\n try {\n const content = await readFile(filePath, 'utf-8');\n if (content.length === 0) return 0;\n let count = 0;\n for (let i = 0; i < content.length; i++) {\n if (content.charCodeAt(i) === 10) count++;\n }\n // A file with no trailing newline has one more line than newline count\n if (content.charCodeAt(content.length - 1) !== 10) count++;\n return count;\n } catch {\n return 0;\n }\n}\n\n/**\n * Collects root-level source file names from the project directory.\n *\n * @param projectPath - Absolute path to the project root.\n * @returns Array of source file names in the root directory.\n */\nasync function getRootSourceFiles(projectPath: string): Promise<string[]> {\n try {\n const entries = await readdir(projectPath, { withFileTypes: true });\n const sourceFiles: string[] = [];\n for (const entry of entries) {\n if (entry.isFile()) {\n const ext = extname(entry.name);\n if (SOURCE_EXTENSIONS.has(ext)) {\n sourceFiles.push(entry.name);\n }\n }\n }\n return sourceFiles;\n } catch {\n return [];\n }\n}\n\n/**\n * Computes quantitative statistics about a project's source files.\n *\n * Reads each source file and produces aggregate metrics including\n * file counts, line counts, and extension breakdown.\n *\n * @param projectPath - Absolute path to the project root.\n * @param dirs - Pre-walked directory list. If not provided, walks the directory tree.\n * @returns Statistics about the codebase.\n */\nexport async function computeStatistics(\n projectPath: string,\n dirs?: WalkedDirectory[],\n): Promise<CodebaseStatistics> {\n const directories = dirs ?? (await walkDirectory(projectPath));\n const rootFiles = await getRootSourceFiles(projectPath);\n\n // Collect all file paths and extensions\n const filesToProcess: Array<{ relativePath: string; absolutePath: string; ext: string }> = [];\n\n // Root-level source files\n for (const name of rootFiles) {\n filesToProcess.push({\n relativePath: name,\n absolutePath: join(projectPath, name),\n ext: extname(name),\n });\n }\n\n // Files from subdirectories\n for (const dir of directories) {\n for (const name of dir.sourceFileNames) {\n filesToProcess.push({\n relativePath: `${dir.relativePath}/${name}`,\n absolutePath: join(dir.absolutePath, name),\n ext: extname(name),\n });\n }\n }\n\n const totalFiles = filesToProcess.length;\n\n if (totalFiles === 0) {\n return {\n totalFiles: 0,\n totalLines: 0,\n averageFileLines: 0,\n largestFiles: [],\n filesByExtension: {},\n };\n }\n\n // Count lines for all files concurrently\n const lineResults = await Promise.all(\n filesToProcess.map(async (file) => ({\n path: file.relativePath,\n lines: await countLines(file.absolutePath),\n ext: file.ext,\n })),\n );\n\n let totalLines = 0;\n const filesByExtension: Record<string, number> = {};\n const allFiles: FileStatistic[] = [];\n\n for (const result of lineResults) {\n totalLines += result.lines;\n filesByExtension[result.ext] = (filesByExtension[result.ext] ?? 0) + 1;\n allFiles.push({ path: result.path, lines: result.lines });\n }\n\n // Sort descending by lines, take top 5\n allFiles.sort((a, b) => b.lines - a.lines);\n const largestFiles = allFiles.slice(0, 5);\n\n return {\n totalFiles,\n totalLines,\n averageFileLines: Math.round(totalLines / totalFiles),\n largestFiles,\n filesByExtension,\n };\n}\n","import { readdir } from 'node:fs/promises';\nimport { join, relative } from 'node:path';\n\n/** Directories to always skip during scanning. */\nconst IGNORED_DIRS = new Set([\n 'node_modules',\n '.git',\n 'dist',\n 'build',\n '.next',\n '.nuxt',\n '.viberails',\n 'coverage',\n '.turbo',\n '.cache',\n '.output',\n '.expo',\n 'android',\n 'ios',\n 'Pods',\n '.gradle',\n]);\n\n/** Source file extensions to count. */\nexport const SOURCE_EXTENSIONS = new Set([\n '.ts',\n '.tsx',\n '.js',\n '.jsx',\n '.mjs',\n '.cjs',\n '.vue',\n '.svelte',\n '.astro',\n]);\n\nexport interface WalkedDirectory {\n /** Path relative to project root, using forward slashes. */\n relativePath: string;\n /** Absolute path. */\n absolutePath: string;\n /** Number of source files directly in this directory. */\n sourceFileCount: number;\n /** Names of source files in this directory. */\n sourceFileNames: string[];\n /** Depth relative to the project root (1 = direct children). */\n depth: number;\n}\n\n/**\n * Walks a project directory tree using BFS, collecting directory info.\n *\n * @param projectPath - Absolute path to the project root.\n * @param maxDepth - Maximum directory depth to traverse (default 4).\n * @returns Flat list of all visited directories (excluding root itself).\n */\nexport async function walkDirectory(\n projectPath: string,\n maxDepth: number = 4,\n): Promise<WalkedDirectory[]> {\n const results: WalkedDirectory[] = [];\n const queue: Array<{ absolutePath: string; depth: number }> = [];\n\n try {\n const rootEntries = await readdir(projectPath, { withFileTypes: true });\n for (const entry of rootEntries) {\n if (entry.isDirectory() && !entry.isSymbolicLink() && !IGNORED_DIRS.has(entry.name)) {\n queue.push({ absolutePath: join(projectPath, entry.name), depth: 1 });\n }\n }\n } catch {\n return results;\n }\n\n while (queue.length > 0) {\n const item = queue.shift();\n if (!item) break;\n const { absolutePath, depth } = item;\n const sourceFileNames: string[] = [];\n\n try {\n const entries = await readdir(absolutePath, { withFileTypes: true });\n\n for (const entry of entries) {\n if (entry.isSymbolicLink()) continue;\n\n if (entry.isDirectory() && depth < maxDepth && !IGNORED_DIRS.has(entry.name)) {\n queue.push({ absolutePath: join(absolutePath, entry.name), depth: depth + 1 });\n } else if (entry.isFile()) {\n const dotIndex = entry.name.lastIndexOf('.');\n if (dotIndex > 0) {\n const ext = entry.name.substring(dotIndex);\n if (SOURCE_EXTENSIONS.has(ext)) {\n sourceFileNames.push(entry.name);\n }\n }\n }\n }\n } catch {\n continue;\n }\n\n const rel = relative(projectPath, absolutePath).split('\\\\').join('/');\n results.push({\n relativePath: rel,\n absolutePath,\n sourceFileCount: sourceFileNames.length,\n sourceFileNames,\n depth,\n });\n }\n\n return results;\n}\n","import { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { DetectedConvention, DetectedStructure } from '@viberails/types';\nimport { confidenceFromConsistency } from '@viberails/types';\nimport { classifyFilename } from './utils/classify-filename.js';\nimport type { WalkedDirectory } from './utils/walk-directory.js';\nimport { walkDirectory } from './utils/walk-directory.js';\n\n/**\n * Detects coding conventions used in a project by analyzing file names\n * and configuration files.\n *\n * @param projectPath - Absolute path to the project root directory.\n * @param structure - Previously detected directory structure, used to identify\n * directories by role.\n * @param dirs - Pre-walked directory list. If not provided, walks the directory tree.\n * @returns A record of detected conventions keyed by convention name.\n * Only statistical conventions with sampleSize >= 3 are included.\n */\nexport async function detectConventions(\n projectPath: string,\n structure: DetectedStructure,\n dirs?: WalkedDirectory[],\n): Promise<Record<string, DetectedConvention>> {\n if (!dirs) {\n dirs = await walkDirectory(projectPath, 4);\n }\n\n const result: Record<string, DetectedConvention> = {};\n\n const fileNaming = detectFileNaming(dirs);\n if (fileNaming) result.fileNaming = fileNaming;\n\n const componentNaming = detectComponentNaming(dirs, structure);\n if (componentNaming) result.componentNaming = componentNaming;\n\n const hookNaming = detectHookNaming(dirs, structure);\n if (hookNaming) result.hookNaming = hookNaming;\n\n // importAlias is binary (present or not) — bypasses sampleSize threshold\n const importAlias = await detectImportAlias(projectPath);\n if (importAlias) result.importAlias = importAlias;\n\n return result;\n}\n\n/** Strips the file extension, handling multi-dot names like foo.test.ts. */\nfunction stripExtension(filename: string): string {\n const firstDot = filename.indexOf('.');\n return firstDot > 0 ? filename.substring(0, firstDot) : filename;\n}\n\n/**\n * Detects the dominant file naming convention across all directories\n * with 3+ source files. Only returns conventions with consistency >= 70%\n * (medium or high confidence).\n */\nfunction detectFileNaming(dirs: WalkedDirectory[]): DetectedConvention | undefined {\n const conventionCounts = new Map<string, number>();\n let total = 0;\n\n for (const dir of dirs) {\n if (dir.sourceFileCount < 3) continue;\n\n for (const filename of dir.sourceFileNames) {\n if (filename.includes('.test.') || filename.includes('.spec.')) continue;\n const bare = stripExtension(filename);\n if (bare === 'index') continue;\n\n const convention = classifyFilename(bare);\n total++;\n if (convention !== 'unknown') {\n conventionCounts.set(convention, (conventionCounts.get(convention) ?? 0) + 1);\n }\n }\n }\n\n if (total < 3) return undefined;\n\n const sorted = [...conventionCounts.entries()].sort((a, b) => b[1] - a[1]);\n if (sorted.length === 0) return undefined;\n\n const [dominantConvention, dominantCount] = sorted[0];\n const consistency = Math.round((dominantCount / total) * 100);\n const confidence = confidenceFromConsistency(consistency);\n\n if (confidence === 'low') return undefined;\n\n return {\n value: dominantConvention,\n confidence,\n sampleSize: total,\n consistency,\n };\n}\n\n/**\n * Detects component naming convention by checking if .tsx files in\n * component directories use PascalCase filenames.\n */\nfunction detectComponentNaming(\n dirs: WalkedDirectory[],\n structure: DetectedStructure,\n): DetectedConvention | undefined {\n const componentPaths = new Set(\n structure.directories.filter((d) => d.role === 'components').map((d) => d.path),\n );\n\n const tsxFiles: string[] = [];\n for (const dir of dirs) {\n if (!componentPaths.has(dir.relativePath)) continue;\n for (const f of dir.sourceFileNames) {\n if (f.endsWith('.tsx')) tsxFiles.push(f);\n }\n }\n\n if (tsxFiles.length < 3) return undefined;\n\n const pascalCount = tsxFiles.filter((f) => /^[A-Z]/.test(stripExtension(f))).length;\n const consistency = Math.round((pascalCount / tsxFiles.length) * 100);\n const dominantValue = pascalCount >= tsxFiles.length / 2 ? 'PascalCase' : 'camelCase';\n\n return {\n value: dominantValue,\n confidence: confidenceFromConsistency(consistency),\n sampleSize: tsxFiles.length,\n consistency,\n };\n}\n\n/**\n * Detects hook naming convention by checking if hook files use\n * kebab-case (use-*) or camelCase (useXxx) prefix.\n */\nfunction detectHookNaming(\n dirs: WalkedDirectory[],\n structure: DetectedStructure,\n): DetectedConvention | undefined {\n const hookPaths = new Set(\n structure.directories.filter((d) => d.role === 'hooks').map((d) => d.path),\n );\n\n const hookFiles: string[] = [];\n for (const dir of dirs) {\n if (!hookPaths.has(dir.relativePath)) continue;\n for (const f of dir.sourceFileNames) {\n const bare = stripExtension(f);\n if (bare.startsWith('use-') || /^use[A-Z]/.test(bare)) {\n hookFiles.push(f);\n }\n }\n }\n\n if (hookFiles.length < 3) return undefined;\n\n let kebabCount = 0;\n let camelCount = 0;\n\n for (const filename of hookFiles) {\n const bare = stripExtension(filename);\n if (bare.startsWith('use-')) {\n kebabCount++;\n } else if (/^use[A-Z]/.test(bare)) {\n camelCount++;\n }\n }\n\n const total = hookFiles.length;\n const isDominantKebab = kebabCount >= camelCount;\n const dominantCount = isDominantKebab ? kebabCount : camelCount;\n const consistency = Math.round((dominantCount / total) * 100);\n\n return {\n value: isDominantKebab ? 'use-*' : 'useXxx',\n confidence: confidenceFromConsistency(consistency),\n sampleSize: total,\n consistency,\n };\n}\n\n/** Minimal shape for the tsconfig subset we read. */\ninterface TsConfigSubset {\n compilerOptions?: {\n paths?: Record<string, string[]>;\n };\n}\n\n/**\n * Detects import alias patterns from tsconfig.json or jsconfig.json paths configuration.\n * Checks tsconfig.json first, then falls back to jsconfig.json for JavaScript projects.\n */\nasync function detectImportAlias(projectPath: string): Promise<DetectedConvention | undefined> {\n for (const configFile of ['tsconfig.json', 'jsconfig.json']) {\n try {\n const raw = await readFile(join(projectPath, configFile), 'utf-8');\n const config = JSON.parse(raw) as TsConfigSubset;\n const paths = config.compilerOptions?.paths;\n if (!paths) continue;\n\n const aliases = Object.keys(paths);\n if (aliases.length === 0) continue;\n\n return {\n value: aliases.join(','),\n confidence: 'high',\n sampleSize: aliases.length,\n consistency: 100,\n };\n } catch {}\n }\n return undefined;\n}\n","/**\n * Naming convention types for filenames.\n */\nexport type FilenameConvention =\n | 'kebab-case'\n | 'camelCase'\n | 'PascalCase'\n | 'snake_case'\n | 'unknown';\n\nconst PATTERNS = [\n { convention: 'PascalCase', regex: /^[A-Z][a-zA-Z0-9]*$/ },\n { convention: 'camelCase', regex: /^[a-z][a-zA-Z0-9]*[A-Z][a-zA-Z0-9]*$/ },\n { convention: 'kebab-case', regex: /^[a-z][a-z0-9]*(-[a-z0-9]+)+$/ },\n { convention: 'snake_case', regex: /^[a-z][a-z0-9]*(_[a-z0-9]+)+$/ },\n] as const;\n\n/**\n * Classifies a filename (without extension) into a naming convention.\n *\n * Single lowercase words like 'utils' return 'unknown' because they are\n * ambiguous — they could be kebab-case, snake_case, or camelCase without\n * a disambiguating structural marker.\n *\n * @param filename - The bare filename with no extension (e.g. 'user-profile').\n * @returns The detected naming convention, or 'unknown' if ambiguous.\n */\nexport function classifyFilename(filename: string): FilenameConvention {\n for (const { convention, regex } of PATTERNS) {\n if (regex.test(filename)) return convention;\n }\n return 'unknown';\n}\n","import { access } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { DetectedStack, StackItem } from '@viberails/types';\nimport {\n BACKEND_MAPPINGS,\n FRAMEWORK_MAPPINGS,\n LIBRARY_MAPPINGS,\n LOCK_FILE_MAP,\n ORM_MAPPINGS,\n STYLING_MAPPINGS,\n} from './stack-mappings.js';\nimport { readPackageJson } from './utils/read-package-json.js';\n\n/**\n * Extracts the major version number from a semver range string.\n *\n * @param range - A semver range such as `\"^15.0.3\"`, `\"~2.1.0\"`, or `\"3.x\"`.\n * @returns The major version string (e.g. `\"15\"`), or `undefined` if extraction fails.\n */\nexport function extractMajorVersion(range: string): string | undefined {\n const match = range.match(/(\\d+)/);\n return match?.[1];\n}\n\n/** Check whether a file exists at the given path. */\nasync function fileExists(filePath: string): Promise<boolean> {\n try {\n await access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Detects the technology stack of a project by reading its package.json\n * and checking for lock files and configuration files.\n *\n * When `additionalDeps` is provided, they are merged as a base layer\n * beneath the package's own deps. This allows monorepo root-level deps\n * (e.g. typescript, eslint) to be visible during per-package scanning.\n *\n * @param projectPath - Absolute path to the project root directory.\n * @param additionalDeps - Optional base dependencies merged under package deps.\n * @returns The detected technology stack.\n */\nexport async function detectStack(\n projectPath: string,\n additionalDeps?: Record<string, string>,\n): Promise<DetectedStack> {\n const pkg = await readPackageJson(projectPath);\n const allDeps: Record<string, string> = {\n ...additionalDeps,\n ...pkg?.dependencies,\n ...pkg?.devDependencies,\n };\n\n const framework = detectFramework(allDeps);\n const language = await detectLanguage(projectPath, allDeps);\n const styling = detectFirst(allDeps, STYLING_MAPPINGS);\n const backend = detectFirst(allDeps, BACKEND_MAPPINGS);\n const orm = detectFirst(allDeps, ORM_MAPPINGS);\n const packageManager = await detectPackageManager(projectPath);\n const linter = detectLinter(allDeps);\n const formatter = detectFormatter(allDeps);\n const testRunner = detectTestRunner(allDeps);\n const libraries = detectLibraries(allDeps);\n\n return {\n ...(framework && { framework }),\n language,\n ...(styling && { styling }),\n ...(backend && { backend }),\n ...(orm && { orm }),\n packageManager,\n ...(linter && { linter }),\n ...(formatter && { formatter }),\n ...(testRunner && { testRunner }),\n libraries,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Individual detectors\n// ---------------------------------------------------------------------------\n\nfunction detectFramework(allDeps: Record<string, string>): StackItem | undefined {\n for (const mapping of FRAMEWORK_MAPPINGS) {\n if (!(mapping.dep in allDeps)) continue;\n if (mapping.excludeDeps?.some((dep) => dep in allDeps)) continue;\n return {\n name: mapping.name,\n version: extractMajorVersion(allDeps[mapping.dep]),\n };\n }\n return undefined;\n}\n\nasync function detectLanguage(\n projectPath: string,\n allDeps: Record<string, string>,\n): Promise<StackItem> {\n if ('typescript' in allDeps) {\n return {\n name: 'typescript',\n version: extractMajorVersion(allDeps.typescript),\n };\n }\n if (await fileExists(join(projectPath, 'tsconfig.json'))) {\n return { name: 'typescript' };\n }\n return { name: 'javascript' };\n}\n\nfunction detectFirst(\n allDeps: Record<string, string>,\n mappings: Array<{ dep: string; name: string }>,\n): StackItem | undefined {\n for (const mapping of mappings) {\n if (mapping.dep in allDeps) {\n return {\n name: mapping.name,\n version: extractMajorVersion(allDeps[mapping.dep]),\n };\n }\n }\n return undefined;\n}\n\nasync function detectPackageManager(projectPath: string): Promise<StackItem> {\n for (const entry of LOCK_FILE_MAP) {\n if (await fileExists(join(projectPath, entry.file))) {\n return { name: entry.name };\n }\n }\n return { name: 'npm' };\n}\n\nfunction detectLinter(allDeps: Record<string, string>): StackItem | undefined {\n if ('eslint' in allDeps) {\n return { name: 'eslint', version: extractMajorVersion(allDeps.eslint) };\n }\n if ('@biomejs/biome' in allDeps) {\n return {\n name: 'biome',\n version: extractMajorVersion(allDeps['@biomejs/biome']),\n };\n }\n return undefined;\n}\n\nfunction detectFormatter(allDeps: Record<string, string>): StackItem | undefined {\n if ('prettier' in allDeps) {\n return { name: 'prettier', version: extractMajorVersion(allDeps.prettier) };\n }\n if ('@biomejs/biome' in allDeps) {\n return {\n name: 'biome',\n version: extractMajorVersion(allDeps['@biomejs/biome']),\n };\n }\n return undefined;\n}\n\nfunction detectTestRunner(allDeps: Record<string, string>): StackItem | undefined {\n if ('vitest' in allDeps) {\n return { name: 'vitest', version: extractMajorVersion(allDeps.vitest) };\n }\n if ('jest' in allDeps) {\n return { name: 'jest', version: extractMajorVersion(allDeps.jest) };\n }\n if ('@playwright/test' in allDeps) {\n return { name: 'playwright', version: extractMajorVersion(allDeps['@playwright/test']) };\n }\n if ('cypress' in allDeps) {\n return { name: 'cypress', version: extractMajorVersion(allDeps.cypress) };\n }\n if ('mocha' in allDeps) {\n return { name: 'mocha', version: extractMajorVersion(allDeps.mocha) };\n }\n return undefined;\n}\n\nfunction detectLibraries(allDeps: Record<string, string>): StackItem[] {\n const libs: StackItem[] = [];\n for (const mapping of LIBRARY_MAPPINGS) {\n const found = mapping.deps.find((dep) => dep in allDeps);\n if (found) {\n libs.push({\n name: mapping.name,\n version: extractMajorVersion(allDeps[found]),\n });\n }\n }\n return libs;\n}\n","/**\n * Detection mapping tables used by detectStack to identify frameworks,\n * backends, ORMs, styling solutions, and notable libraries from package.json.\n */\n\nexport interface FrameworkMapping {\n /** Package name to look for in dependencies. */\n dep: string;\n /** Name to use in the StackItem. */\n name: string;\n /** If any of these deps are present, skip this mapping. */\n excludeDeps?: string[];\n}\n\nexport const FRAMEWORK_MAPPINGS: FrameworkMapping[] = [\n { dep: 'next', name: 'nextjs' },\n { dep: 'expo', name: 'expo' },\n { dep: 'react-native', name: 'react-native', excludeDeps: ['expo'] },\n { dep: '@angular/core', name: 'angular' },\n { dep: '@sveltejs/kit', name: 'sveltekit' },\n { dep: 'svelte', name: 'svelte', excludeDeps: ['@sveltejs/kit', 'astro'] },\n { dep: 'astro', name: 'astro' },\n { dep: '@remix-run/react', name: 'remix' },\n { dep: 'nuxt', name: 'nuxt' },\n { dep: 'vue', name: 'vue', excludeDeps: ['nuxt'] },\n { dep: 'gatsby', name: 'gatsby' },\n { dep: 'solid-js', name: 'solidjs' },\n { dep: '@builder.io/qwik', name: 'qwik' },\n { dep: 'electron', name: 'electron' },\n { dep: '@tauri-apps/api', name: 'tauri' },\n {\n dep: 'react',\n name: 'react',\n excludeDeps: ['next', '@remix-run/react', 'gatsby', 'expo'],\n },\n];\n\nexport const BACKEND_MAPPINGS: Array<{ dep: string; name: string }> = [\n { dep: '@nestjs/core', name: 'nestjs' },\n { dep: 'express', name: 'express' },\n { dep: 'fastify', name: 'fastify' },\n { dep: 'koa', name: 'koa' },\n { dep: 'hono', name: 'hono' },\n { dep: '@supabase/supabase-js', name: 'supabase' },\n { dep: 'firebase', name: 'firebase' },\n { dep: 'convex', name: 'convex' },\n];\n\nexport const ORM_MAPPINGS: Array<{ dep: string; name: string }> = [\n { dep: '@prisma/client', name: 'prisma' },\n { dep: 'prisma', name: 'prisma' },\n { dep: 'drizzle-orm', name: 'drizzle' },\n { dep: 'typeorm', name: 'typeorm' },\n { dep: 'sequelize', name: 'sequelize' },\n { dep: 'mongoose', name: 'mongoose' },\n { dep: 'kysely', name: 'kysely' },\n { dep: '@mikro-orm/core', name: 'mikro-orm' },\n];\n\nexport const STYLING_MAPPINGS: Array<{ dep: string; name: string }> = [\n { dep: 'tailwindcss', name: 'tailwindcss' },\n { dep: 'styled-components', name: 'styled-components' },\n { dep: '@emotion/react', name: 'emotion' },\n { dep: 'sass', name: 'sass' },\n { dep: '@vanilla-extract/css', name: 'vanilla-extract' },\n { dep: 'unocss', name: 'unocss' },\n { dep: '@pandacss/dev', name: 'panda-css' },\n { dep: 'nativewind', name: 'nativewind' },\n];\n\nexport const LIBRARY_MAPPINGS: Array<{ deps: string[]; name: string }> = [\n // Validation\n { deps: ['zod'], name: 'zod' },\n // API\n { deps: ['@trpc/server'], name: 'trpc' },\n { deps: ['@tanstack/react-query'], name: 'react-query' },\n { deps: ['@apollo/client'], name: 'apollo' },\n { deps: ['urql'], name: 'urql' },\n { deps: ['graphql'], name: 'graphql' },\n // State management\n { deps: ['@reduxjs/toolkit'], name: 'redux-toolkit' },\n { deps: ['zustand'], name: 'zustand' },\n { deps: ['jotai'], name: 'jotai' },\n { deps: ['recoil'], name: 'recoil' },\n { deps: ['mobx'], name: 'mobx' },\n { deps: ['xstate'], name: 'xstate' },\n { deps: ['valtio'], name: 'valtio' },\n // Forms\n { deps: ['react-hook-form'], name: 'react-hook-form' },\n { deps: ['formik'], name: 'formik' },\n // HTTP\n { deps: ['axios'], name: 'axios' },\n // Auth\n { deps: ['next-auth'], name: 'next-auth' },\n { deps: ['@auth/core'], name: 'auth-js' },\n { deps: ['@clerk/nextjs'], name: 'clerk' },\n { deps: ['lucia'], name: 'lucia' },\n // Dates\n { deps: ['date-fns'], name: 'date-fns' },\n { deps: ['dayjs'], name: 'dayjs' },\n { deps: ['luxon'], name: 'luxon' },\n // i18n\n { deps: ['i18next'], name: 'i18next' },\n { deps: ['next-i18next'], name: 'next-i18next' },\n // Payments\n { deps: ['stripe'], name: 'stripe' },\n // Realtime\n { deps: ['socket.io'], name: 'socket.io' },\n // Testing utilities\n { deps: ['@testing-library/react'], name: 'testing-library' },\n { deps: ['msw'], name: 'msw' },\n { deps: ['storybook', '@storybook/react'], name: 'storybook' },\n // Bundlers\n { deps: ['vite'], name: 'vite' },\n { deps: ['webpack'], name: 'webpack' },\n { deps: ['esbuild'], name: 'esbuild' },\n { deps: ['@rspack/core'], name: 'rspack' },\n // Monorepo\n { deps: ['nx'], name: 'nx' },\n { deps: ['lerna'], name: 'lerna' },\n];\n\nexport const LOCK_FILE_MAP: Array<{ file: string; name: string }> = [\n { file: 'pnpm-lock.yaml', name: 'pnpm' },\n { file: 'yarn.lock', name: 'yarn' },\n { file: 'bun.lockb', name: 'bun' },\n { file: 'package-lock.json', name: 'npm' },\n];\n","import { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\n/**\n * Minimal interface for the fields we read from package.json.\n */\nexport interface PackageJson {\n name?: string;\n version?: string;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n}\n\n/**\n * Safely reads and parses a package.json file from the given directory.\n *\n * @param projectPath - Absolute path to the project root directory.\n * @returns Parsed package.json contents, or `null` if the file doesn't exist or is invalid JSON.\n */\nexport async function readPackageJson(projectPath: string): Promise<PackageJson | null> {\n try {\n const raw = await readFile(join(projectPath, 'package.json'), 'utf-8');\n return JSON.parse(raw) as PackageJson;\n } catch {\n return null;\n }\n}\n","import type { DetectedConvention, DetectedStructure } from '@viberails/types';\nimport { confidenceFromConsistency } from '@viberails/types';\nimport { classifyDirectory } from './utils/classify-directory.js';\nimport type { WalkedDirectory } from './utils/walk-directory.js';\nimport { walkDirectory } from './utils/walk-directory.js';\n\n/**\n * Detects the directory structure and organization of a project.\n *\n * Classifies directories by role, detects whether a src/ directory is in use,\n * and identifies test file patterns.\n *\n * @param projectPath - Absolute path to the project root directory.\n * @param dirs - Pre-walked directory list. If not provided, walks the directory tree.\n * @returns The detected directory structure.\n */\nexport async function detectStructure(\n projectPath: string,\n dirs?: WalkedDirectory[],\n): Promise<DetectedStructure> {\n if (!dirs) {\n dirs = await walkDirectory(projectPath, 4);\n }\n\n // Detect srcDir\n const hasSrcDir = dirs.some((d) => d.relativePath === 'src' || d.relativePath.startsWith('src/'));\n const srcDir = hasSrcDir ? 'src' : undefined;\n\n // Classify directories\n const directories = dirs.map((d) => classifyDirectory(d)).filter((d) => d !== null);\n\n // Detect test pattern\n const testPattern = detectTestPattern(dirs);\n\n return {\n ...(srcDir !== undefined && { srcDir }),\n directories,\n ...(testPattern !== undefined && { testPattern }),\n };\n}\n\n/**\n * Detects the dominant test file naming pattern from all source files.\n * Returns undefined if fewer than 3 test files are found.\n */\nfunction detectTestPattern(\n dirs: Array<{ sourceFileNames: string[] }>,\n): DetectedConvention<string> | undefined {\n const allFiles = dirs.flatMap((d) => d.sourceFileNames);\n const testFiles = allFiles.filter((f) => f.includes('.test.') || f.includes('.spec.'));\n\n if (testFiles.length < 3) return undefined;\n\n const dotTestCount = testFiles.filter((f) => f.includes('.test.')).length;\n const dotSpecCount = testFiles.filter((f) => f.includes('.spec.')).length;\n\n const isDotTest = dotTestCount >= dotSpecCount;\n const dominantSep = isDotTest ? '.test.' : '.spec.';\n const dominantCount = isDotTest ? dotTestCount : dotSpecCount;\n const consistency = Math.round((dominantCount / testFiles.length) * 100);\n\n // Find most common extension among dominant test files\n const extCounts = new Map<string, number>();\n for (const f of testFiles.filter((f) => f.includes(dominantSep))) {\n const ext = f.substring(f.lastIndexOf('.'));\n extCounts.set(ext, (extCounts.get(ext) ?? 0) + 1);\n }\n const topExt = [...extCounts.entries()].sort((a, b) => b[1] - a[1])[0]?.[0] ?? '.ts';\n\n const sep = isDotTest ? 'test' : 'spec';\n\n return {\n value: `*.${sep}${topExt}`,\n confidence: confidenceFromConsistency(consistency),\n sampleSize: testFiles.length,\n consistency,\n };\n}\n","import type { Confidence, DirectoryRole } from '@viberails/types';\nimport type { WalkedDirectory } from './walk-directory.js';\n\nexport interface ClassifiedDirectory {\n path: string;\n role: DirectoryRole;\n fileCount: number;\n confidence: Confidence;\n}\n\ninterface RolePattern {\n role: DirectoryRole;\n pathPatterns: string[];\n}\n\nconst ROLE_PATTERNS: RolePattern[] = [\n { role: 'pages', pathPatterns: ['src/app', 'src/pages', 'app', 'pages'] },\n { role: 'components', pathPatterns: ['src/components', 'components'] },\n { role: 'hooks', pathPatterns: ['src/hooks', 'hooks'] },\n {\n role: 'utils',\n pathPatterns: ['src/lib', 'src/utils', 'src/helpers', 'lib', 'utils', 'helpers'],\n },\n { role: 'types', pathPatterns: ['src/types', 'types', 'src/@types', '@types'] },\n { role: 'tests', pathPatterns: ['__tests__', 'tests', 'test', 'src/__tests__', 'src/tests'] },\n { role: 'styles', pathPatterns: ['src/styles', 'styles', 'src/css', 'css'] },\n { role: 'api', pathPatterns: ['src/api', 'api', 'src/app/api', 'app/api'] },\n { role: 'config', pathPatterns: ['config', 'src/config'] },\n];\n\n/**\n * Classifies a directory by its role in the project.\n *\n * @param dir - A walked directory entry.\n * @returns Classified directory info, or null if the directory should be skipped.\n */\nexport function classifyDirectory(dir: WalkedDirectory): ClassifiedDirectory | null {\n // Try name-based matching first\n const nameMatch = matchByName(dir.relativePath);\n if (nameMatch) {\n const confidence: Confidence = dir.sourceFileCount > 0 ? 'high' : 'low';\n return {\n path: dir.relativePath,\n role: nameMatch,\n fileCount: dir.sourceFileCount,\n confidence,\n };\n }\n\n // Skip directories with no source files if no name match\n if (dir.sourceFileCount === 0) return null;\n\n // Content-based heuristics\n const contentRole = inferFromContent(dir);\n if (contentRole) return contentRole;\n\n // Has source files but no classification\n return {\n path: dir.relativePath,\n role: 'unknown',\n fileCount: dir.sourceFileCount,\n confidence: 'low',\n };\n}\n\n/**\n * Matches a relative path against known role patterns.\n * Supports suffix matching so monorepo paths like `apps/web/lib`\n * match patterns like `lib`.\n * Returns the role if matched, or null.\n */\nfunction matchByName(relativePath: string): DirectoryRole | null {\n for (const { role, pathPatterns } of ROLE_PATTERNS) {\n for (const pattern of pathPatterns) {\n if (relativePath === pattern || relativePath.endsWith(`/${pattern}`)) {\n return role;\n }\n }\n }\n return null;\n}\n\n/**\n * Infers directory role from file contents/names.\n */\nfunction inferFromContent(dir: WalkedDirectory): ClassifiedDirectory | null {\n const { sourceFileNames, sourceFileCount } = dir;\n\n // Check for hook files (use-* kebab or useXxx camelCase prefix)\n // Require at least 2 matching files to avoid misclassifying directories\n // with a single hook utility (e.g. lib/ with one useXxx file)\n const hookFiles = sourceFileNames.filter((f) => {\n const name = f.split('.')[0];\n return name.startsWith('use-') || /^use[A-Z]/.test(name);\n });\n if (hookFiles.length >= 2 && hookFiles.length / sourceFileCount >= 0.5) {\n return {\n path: dir.relativePath,\n role: 'hooks',\n fileCount: sourceFileCount,\n confidence: hookFiles.length / sourceFileCount >= 0.9 ? 'high' : 'medium',\n };\n }\n\n // Check for test files — require at least 2 to avoid false positives\n const testFiles = sourceFileNames.filter((f) => f.includes('.test.') || f.includes('.spec.'));\n if (testFiles.length >= 2 && testFiles.length / sourceFileCount >= 0.5) {\n return {\n path: dir.relativePath,\n role: 'tests',\n fileCount: sourceFileCount,\n confidence: testFiles.length / sourceFileCount >= 0.9 ? 'high' : 'medium',\n };\n }\n\n return null;\n}\n","import { readdir, readFile } from 'node:fs/promises';\nimport { join, relative } from 'node:path';\nimport type { DetectedWorkspace, WorkspacePackage } from '@viberails/types';\nimport { readPackageJson } from './utils/read-package-json.js';\n\n/**\n * Detects workspace configuration for monorepo projects.\n *\n * Checks for `pnpm-workspace.yaml` first, then falls back to\n * `package.json` `workspaces` field. Returns `undefined` for\n * single-package projects.\n *\n * @param projectRoot - Absolute path to the project root.\n * @returns Detected workspace info, or `undefined` if not a monorepo.\n */\nexport async function detectWorkspace(projectRoot: string): Promise<DetectedWorkspace | undefined> {\n const patterns = await readWorkspacePatterns(projectRoot);\n if (!patterns || patterns.length === 0) return undefined;\n\n const packageDirs = await resolvePatterns(projectRoot, patterns);\n const packages = await resolvePackages(projectRoot, packageDirs);\n\n if (packages.length === 0) return undefined;\n\n // Resolve internal deps: check if any dependency name matches a workspace package\n const packageNames = new Set(packages.map((p) => p.name));\n for (const pkg of packages) {\n pkg.internalDeps = pkg.internalDeps.filter((dep) => packageNames.has(dep));\n }\n\n return { patterns, packages };\n}\n\n/**\n * Reads workspace glob patterns from pnpm-workspace.yaml or package.json.\n */\nasync function readWorkspacePatterns(projectRoot: string): Promise<string[] | undefined> {\n // Try pnpm-workspace.yaml first\n try {\n const yaml = await readFile(join(projectRoot, 'pnpm-workspace.yaml'), 'utf-8');\n return parsePnpmWorkspaceYaml(yaml);\n } catch {\n // Not found, try package.json\n }\n\n // Fall back to package.json workspaces field\n const pkg = await readPackageJson(projectRoot);\n if (!pkg) return undefined;\n\n const raw = pkg as Record<string, unknown>;\n const workspaces = raw.workspaces;\n\n if (Array.isArray(workspaces)) {\n return workspaces.filter((w): w is string => typeof w === 'string');\n }\n\n // Handle { packages: [...] } format\n if (workspaces && typeof workspaces === 'object' && 'packages' in workspaces) {\n const nested = (workspaces as { packages: unknown }).packages;\n if (Array.isArray(nested)) {\n return nested.filter((w): w is string => typeof w === 'string');\n }\n }\n\n return undefined;\n}\n\n/**\n * Parses workspace patterns from pnpm-workspace.yaml content.\n * Handles the common format: `packages:\\n - 'packages/*'`\n */\nfunction parsePnpmWorkspaceYaml(content: string): string[] {\n const patterns: string[] = [];\n let inPackages = false;\n\n for (const line of content.split('\\n')) {\n const trimmed = line.trim();\n\n if (trimmed === 'packages:') {\n inPackages = true;\n continue;\n }\n\n // Stop at next top-level key\n if (inPackages && trimmed.length > 0 && !trimmed.startsWith('-')) {\n break;\n }\n\n if (inPackages && trimmed.startsWith('-')) {\n // Extract the pattern, stripping quotes and leading dash\n const value = trimmed\n .slice(1)\n .trim()\n .replace(/^['\"]|['\"]$/g, '');\n if (value) patterns.push(value);\n }\n }\n\n return patterns;\n}\n\n/**\n * Resolves workspace glob patterns to actual directory paths.\n * Supports simple `*` wildcard matching (e.g. `packages/*`).\n */\nasync function resolvePatterns(projectRoot: string, patterns: string[]): Promise<string[]> {\n const dirs: string[] = [];\n\n for (const pattern of patterns) {\n if (pattern.endsWith('/*')) {\n // Simple wildcard: list children of the parent directory\n const parent = join(projectRoot, pattern.slice(0, -2));\n try {\n const entries = await readdir(parent, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory()) {\n dirs.push(join(parent, entry.name));\n }\n }\n } catch {\n // Parent directory doesn't exist, skip\n }\n } else {\n // Literal path\n dirs.push(join(projectRoot, pattern));\n }\n }\n\n return dirs;\n}\n\n/**\n * Resolves workspace directories to WorkspacePackage objects.\n * Skips directories without a valid package.json.\n */\nasync function resolvePackages(projectRoot: string, dirs: string[]): Promise<WorkspacePackage[]> {\n const packages: WorkspacePackage[] = [];\n\n for (const dir of dirs) {\n const pkg = await readPackageJson(dir);\n if (!pkg?.name) continue;\n\n // Collect all dependency names as potential internal deps\n const allDeps = [\n ...Object.keys(pkg.dependencies ?? {}),\n ...Object.keys(pkg.devDependencies ?? {}),\n ];\n\n packages.push({\n name: pkg.name,\n path: dir,\n relativePath: relative(projectRoot, dir),\n internalDeps: allDeps,\n });\n }\n\n return packages;\n}\n","import { stat } from 'node:fs/promises';\nimport { basename, resolve } from 'node:path';\nimport type { ScanResult } from '@viberails/types';\nimport {\n aggregateConventions,\n aggregateStacks,\n aggregateStatistics,\n aggregateStructures,\n} from './aggregate.js';\nimport { detectWorkspace } from './detect-workspace.js';\nimport { scanPackage } from './scan-package.js';\nimport { readPackageJson } from './utils/read-package-json.js';\n\n/**\n * Options for the scan function.\n */\nexport type ScanOptions = Record<string, never>;\n\n/**\n * Scans a project directory and returns a comprehensive analysis of its\n * stack, structure, conventions, and statistics.\n *\n * For monorepos, each workspace package is scanned independently and\n * results are aggregated. For single-package projects, the root is\n * scanned as a single package.\n *\n * @param projectPath - Absolute or relative path to the project root.\n * @param options - Optional scan configuration.\n * @returns Complete scan result for the project.\n * @throws If the project path does not exist or is not a directory.\n */\nexport async function scan(projectPath: string, _options?: ScanOptions): Promise<ScanResult> {\n const root = resolve(projectPath);\n\n // Validate that the path exists and is a directory\n try {\n const st = await stat(root);\n if (!st.isDirectory()) {\n throw new Error(`Project path is not a directory: ${root}`);\n }\n } catch (err) {\n if (err instanceof Error && err.message.startsWith('Project path is not')) {\n throw err;\n }\n throw new Error(`Project path does not exist: ${root}`);\n }\n\n const workspace = await detectWorkspace(root);\n\n if (workspace && workspace.packages.length > 0) {\n // Read root deps for sharing with workspace packages\n const rootPkg = await readPackageJson(root);\n const rootDeps: Record<string, string> = {\n ...rootPkg?.dependencies,\n ...rootPkg?.devDependencies,\n };\n\n // Scan each workspace package in parallel\n const packages = await Promise.all(\n workspace.packages.map((wp) => scanPackage(wp.path, wp.name, wp.relativePath, rootDeps)),\n );\n\n return {\n root,\n stack: aggregateStacks(packages),\n structure: aggregateStructures(packages),\n conventions: aggregateConventions(packages),\n statistics: aggregateStatistics(packages),\n workspace,\n packages,\n };\n }\n\n // Single-package project\n const rootPkg = await readPackageJson(root);\n const name = rootPkg?.name ?? basename(root);\n const pkg = await scanPackage(root, name, '');\n\n return {\n root,\n stack: pkg.stack,\n structure: pkg.structure,\n conventions: pkg.conventions,\n statistics: pkg.statistics,\n packages: [pkg],\n };\n}\n","import { resolve } from 'node:path';\nimport type { PackageScanResult } from '@viberails/types';\nimport { computeStatistics } from './compute-statistics.js';\nimport { detectConventions } from './detect-conventions.js';\nimport { detectStack } from './detect-stack.js';\nimport { detectStructure } from './detect-structure.js';\nimport { walkDirectory } from './utils/walk-directory.js';\n\n/**\n * Scans a single package directory and returns per-package scan results.\n *\n * Reuses all existing detector functions scoped to the package directory.\n * In monorepos, `rootDeps` provides root-level dependencies (e.g. typescript,\n * eslint) as a base layer — package-specific deps overlay on top.\n *\n * @param packagePath - Absolute path to the package directory.\n * @param name - Package name from package.json.\n * @param relativePath - Path relative to workspace root (empty string for single-package).\n * @param rootDeps - Optional root-level dependencies merged as a base layer.\n * @returns Per-package scan result.\n */\nexport async function scanPackage(\n packagePath: string,\n name: string,\n relativePath: string,\n rootDeps?: Record<string, string>,\n): Promise<PackageScanResult> {\n const root = resolve(packagePath);\n const dirs = await walkDirectory(root, 4);\n\n const [stack, structure, statistics] = await Promise.all([\n detectStack(root, rootDeps),\n detectStructure(root, dirs),\n computeStatistics(root, dirs),\n ]);\n\n const conventions = await detectConventions(root, structure, dirs);\n\n return {\n name,\n root,\n relativePath,\n stack,\n structure,\n conventions,\n statistics,\n };\n}\n","declare const __PACKAGE_VERSION__: string;\nexport const VERSION: string = __PACKAGE_VERSION__;\n\nexport {\n aggregateConventions,\n aggregateStacks,\n aggregateStatistics,\n aggregateStructures,\n} from './aggregate.js';\nexport { computeStatistics } from './compute-statistics.js';\nexport { detectConventions } from './detect-conventions.js';\nexport { detectStack, extractMajorVersion } from './detect-stack.js';\nexport { detectStructure } from './detect-structure.js';\nexport { detectWorkspace } from './detect-workspace.js';\nexport type { ScanOptions } from './scan.js';\nexport { scan } from './scan.js';\nexport { scanPackage } from './scan-package.js';\nexport type { PackageJson } from './utils/read-package-json.js';\nexport { readPackageJson } from './utils/read-package-json.js';\nexport type { WalkedDirectory } from './utils/walk-directory.js';\nexport { walkDirectory } from './utils/walk-directory.js';\n"],"mappings":"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","names":["readdir","join","readdir","join","readFile","join","confidenceFromConsistency","confidenceFromConsistency","readFile","join","join","readFile","join","join","confidenceFromConsistency","f","confidenceFromConsistency","readdir","readFile","join","relative","readFile","join","readdir","relative","resolve","resolve","rootPkg"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@viberails/scanner",
3
- "version": "0.2.3",
3
+ "version": "0.3.1",
4
4
  "description": "Project scanning for viberails",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -25,7 +25,7 @@
25
25
  "access": "public"
26
26
  },
27
27
  "dependencies": {
28
- "@viberails/types": "0.2.3"
28
+ "@viberails/types": "0.3.1"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/node": "^25.3.5"