param( [string]$Root = (Get-Location).Path, [switch]$Json ) $ErrorActionPreference = "Stop" if (-not (Test-Path -LiteralPath $Root)) { throw "Root path does not exist: $Root" } $rootPath = (Resolve-Path -LiteralPath $Root).Path $excludedDirs = @(".git", "node_modules", "vendor", "dist", "build", "target", ".next", ".idea", ".vscode") function Get-RelativePath { param([string]$Path) $fullPath = (Resolve-Path -LiteralPath $Path).Path if ($fullPath.StartsWith($rootPath, [System.StringComparison]::OrdinalIgnoreCase)) { return ($fullPath.Substring($rootPath.Length).TrimStart([char[]]"\/") -replace "\\", "/") } return ($fullPath -replace "\\", "/") } function Test-IsExcluded { param([string]$Path) $relativePath = Get-RelativePath -Path $Path $parts = $relativePath -split "/" foreach ($part in $parts) { if ($excludedDirs -contains $part) { return $true } } return $false } $files = @(Get-ChildItem -Path $rootPath -Recurse -File -Force | Where-Object { -not (Test-IsExcluded -Path $_.FullName) }) $directories = @(Get-ChildItem -Path $rootPath -Recurse -Directory -Force | Where-Object { -not (Test-IsExcluded -Path $_.FullName) }) $signals = New-Object System.Collections.Generic.List[object] function Add-Signal { param( [string]$Name, [string]$Confidence, [string]$Reason, [string[]]$Evidence ) $existing = @($signals | Where-Object { $_.name -eq $Name }) if ($existing.Count -gt 0) { return } [void]$signals.Add([pscustomobject]@{ name = $Name confidence = $Confidence reason = $Reason evidence = @($Evidence) }) } $sqlFiles = @($files | Where-Object { $_.Extension -match "^\.(sql|dbml)$" }) $ormFiles = @($files | Where-Object { $_.Name -match "(?i)(schema\.prisma|sequelize|typeorm|entity|model|migration)" }) $dbDirs = @($directories | Where-Object { $_.Name -match "(?i)^(db|database|migrations|migration|models|entities|dao|mapper|repository|doc-sql)$" }) $databaseConfig = @($files | Where-Object { $_.Name -match "(?i)(database|datasource|jdbc|gorm|mybatis|hibernate|prisma|knex|typeorm)" }) if ($sqlFiles.Count -gt 0 -or $ormFiles.Count -gt 0 -or $dbDirs.Count -gt 0 -or $databaseConfig.Count -gt 0) { $evidence = @($sqlFiles + $ormFiles + $databaseConfig | Select-Object -First 8 | ForEach-Object { Get-RelativePath -Path $_.FullName }) $evidence += @($dbDirs | Select-Object -First 5 | ForEach-Object { Get-RelativePath -Path $_.FullName }) Add-Signal -Name "project-has-database" -Confidence "high" -Reason "Database files, ORM files, database directories, or database configs were found." -Evidence $evidence } $tableDesignDocs = @($files | Where-Object { (Get-RelativePath -Path $_.FullName) -match "(?i)(doc-sql|table|schema|database|data-model)" }) if ($tableDesignDocs.Count -gt 0) { Add-Signal -Name "designing-tables" -Confidence "medium" -Reason "Database or table-design documentation paths were found." -Evidence @($tableDesignDocs | Select-Object -First 8 | ForEach-Object { Get-RelativePath -Path $_.FullName }) } $syncFiles = @($files | Where-Object { $_.Name -match "(?i)(sync|import|export|reconcile|recovery|recover|external|serial|code|number)" }) if ($syncFiles.Count -gt 0) { Add-Signal -Name "data-may-sync-or-recover" -Confidence "medium" -Reason "Sync, import/export, recovery, external-code, or number-related names were found." -Evidence @($syncFiles | Select-Object -First 8 | ForEach-Object { Get-RelativePath -Path $_.FullName }) } $apiFiles = @($files | Where-Object { $_.Name -match "(?i)(openapi|swagger|controller|router|route|api)" }) if ($apiFiles.Count -gt 0) { Add-Signal -Name "has-api-contract" -Confidence "medium" -Reason "API contract, router, route, or controller files were found." -Evidence @($apiFiles | Select-Object -First 8 | ForEach-Object { Get-RelativePath -Path $_.FullName }) } $frontendFiles = @($files | Where-Object { $_.Extension -match "^\.(tsx|jsx|vue|svelte)$" -or $_.Name -match "(?i)(component|page|view|route|menu|form)" }) if ($frontendFiles.Count -gt 0) { Add-Signal -Name "has-frontend-ui" -Confidence "medium" -Reason "Frontend page, component, route, menu, or form files were found." -Evidence @($frontendFiles | Select-Object -First 8 | ForEach-Object { Get-RelativePath -Path $_.FullName }) } $result = [pscustomobject]@{ root = $rootPath signals = @($signals.ToArray()) } if ($Json) { ConvertTo-Json -InputObject $result -Depth 8 return } if ($signals.Count -eq 0) { Write-Host "No project signals detected." return } foreach ($signal in $signals) { Write-Host "$($signal.name) [$($signal.confidence)] - $($signal.reason)" foreach ($item in @($signal.evidence)) { Write-Host " - $item" } }