@archpilotlabs/archpilot 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (296) hide show
  1. package/dist/archpilot-cli.js +516 -105
  2. package/dist/cli/src/archpilot-cli.js +5523 -0
  3. package/dist/cli/src/archpilot-cli.js.map +1 -0
  4. package/dist/core/src/adr/adrLoader.js +97 -0
  5. package/dist/core/src/adr/adrLoader.js.map +1 -0
  6. package/dist/core/src/adr/adrParser.js +77 -0
  7. package/dist/core/src/adr/adrParser.js.map +1 -0
  8. package/dist/core/src/adr/adrTemplate.js +45 -0
  9. package/dist/core/src/adr/adrTemplate.js.map +1 -0
  10. package/dist/core/src/adr/adrTypes.js +3 -0
  11. package/dist/core/src/adr/adrTypes.js.map +1 -0
  12. package/dist/core/src/adr/adrValidation.js +160 -0
  13. package/dist/core/src/adr/adrValidation.js.map +1 -0
  14. package/dist/core/src/adr/index.js +9 -0
  15. package/dist/core/src/adr/index.js.map +1 -0
  16. package/dist/core/src/adrManagement.js +174 -0
  17. package/dist/core/src/adrManagement.js.map +1 -0
  18. package/dist/core/src/adrRequirementStatus.js +62 -0
  19. package/dist/core/src/adrRequirementStatus.js.map +1 -0
  20. package/dist/core/src/adrScaffoldGeneration.js +325 -0
  21. package/dist/core/src/adrScaffoldGeneration.js.map +1 -0
  22. package/dist/core/src/apiDesignAnalysis.js +869 -0
  23. package/dist/core/src/apiDesignAnalysis.js.map +1 -0
  24. package/dist/core/src/applicationLayeringAnalysis.js +920 -0
  25. package/dist/core/src/applicationLayeringAnalysis.js.map +1 -0
  26. package/dist/core/src/applyRulePack.js +117 -0
  27. package/dist/core/src/applyRulePack.js.map +1 -0
  28. package/dist/core/src/architectureBaseline.js +271 -0
  29. package/dist/core/src/architectureBaseline.js.map +1 -0
  30. package/dist/core/src/architectureComponentDiscoveryDefaults.js +139 -0
  31. package/dist/core/src/architectureComponentDiscoveryDefaults.js.map +1 -0
  32. package/dist/core/src/architectureDependencyContext.js +316 -0
  33. package/dist/core/src/architectureDependencyContext.js.map +1 -0
  34. package/dist/core/src/architectureDriftDetection.js +96 -0
  35. package/dist/core/src/architectureDriftDetection.js.map +1 -0
  36. package/dist/core/src/architectureDriftTimeline.js +221 -0
  37. package/dist/core/src/architectureDriftTimeline.js.map +1 -0
  38. package/dist/core/src/architectureGovernanceBaseline.js +469 -0
  39. package/dist/core/src/architectureGovernanceBaseline.js.map +1 -0
  40. package/dist/core/src/architectureHealthHistory.js +208 -0
  41. package/dist/core/src/architectureHealthHistory.js.map +1 -0
  42. package/dist/core/src/architectureHealthScore.js +657 -0
  43. package/dist/core/src/architectureHealthScore.js.map +1 -0
  44. package/dist/core/src/architectureInit.js +826 -0
  45. package/dist/core/src/architectureInit.js.map +1 -0
  46. package/dist/core/src/architectureMap.js +727 -0
  47. package/dist/core/src/architectureMap.js.map +1 -0
  48. package/dist/core/src/architectureMigrationPreview.js +467 -0
  49. package/dist/core/src/architectureMigrationPreview.js.map +1 -0
  50. package/dist/core/src/architectureModelRefresh.js +209 -0
  51. package/dist/core/src/architectureModelRefresh.js.map +1 -0
  52. package/dist/core/src/architectureReportWorkflow.js +139 -0
  53. package/dist/core/src/architectureReportWorkflow.js.map +1 -0
  54. package/dist/core/src/architectureValidation.js +3879 -0
  55. package/dist/core/src/architectureValidation.js.map +1 -0
  56. package/dist/core/src/architectureWorkspaceModel.js +166 -0
  57. package/dist/core/src/architectureWorkspaceModel.js.map +1 -0
  58. package/dist/core/src/authorizationTenantAnalysis.js +652 -0
  59. package/dist/core/src/authorizationTenantAnalysis.js.map +1 -0
  60. package/dist/core/src/bootstrapArchitectureConfig.js +150 -0
  61. package/dist/core/src/bootstrapArchitectureConfig.js.map +1 -0
  62. package/dist/core/src/changeRisk/changeRiskAssessment.js +251 -0
  63. package/dist/core/src/changeRisk/changeRiskAssessment.js.map +1 -0
  64. package/dist/core/src/changeRisk/changeRiskTypes.js +3 -0
  65. package/dist/core/src/changeRisk/changeRiskTypes.js.map +1 -0
  66. package/dist/core/src/changeRisk/index.js +6 -0
  67. package/dist/core/src/changeRisk/index.js.map +1 -0
  68. package/dist/core/src/ci/githubInlineAnnotations.js +245 -0
  69. package/dist/core/src/ci/githubInlineAnnotations.js.map +1 -0
  70. package/dist/core/src/ci/githubPrCommentAdapter.js +232 -0
  71. package/dist/core/src/ci/githubPrCommentAdapter.js.map +1 -0
  72. package/dist/core/src/ciWorkflowTemplates.js +1042 -0
  73. package/dist/core/src/ciWorkflowTemplates.js.map +1 -0
  74. package/dist/core/src/compliance/complianceExportBuilder.js +122 -0
  75. package/dist/core/src/compliance/complianceExportBuilder.js.map +1 -0
  76. package/dist/core/src/compliance/complianceExportTypes.js +3 -0
  77. package/dist/core/src/compliance/complianceExportTypes.js.map +1 -0
  78. package/dist/core/src/compliance/complianceExportWriter.js +48 -0
  79. package/dist/core/src/compliance/complianceExportWriter.js.map +1 -0
  80. package/dist/core/src/compliance/complianceRulePackResolver.js +62 -0
  81. package/dist/core/src/compliance/complianceRulePackResolver.js.map +1 -0
  82. package/dist/core/src/config/index.js +17 -0
  83. package/dist/core/src/config/index.js.map +1 -0
  84. package/dist/core/src/config/projectConfig.js +224 -0
  85. package/dist/core/src/config/projectConfig.js.map +1 -0
  86. package/dist/core/src/config/projectIdentity.js +160 -0
  87. package/dist/core/src/config/projectIdentity.js.map +1 -0
  88. package/dist/core/src/dataQueryRiskDetection.js +1203 -0
  89. package/dist/core/src/dataQueryRiskDetection.js.map +1 -0
  90. package/dist/core/src/dependencyValidation.js +2557 -0
  91. package/dist/core/src/dependencyValidation.js.map +1 -0
  92. package/dist/core/src/discoverAdrs.js +15 -0
  93. package/dist/core/src/discoverAdrs.js.map +1 -0
  94. package/dist/core/src/drift/driftComparator.js +49 -0
  95. package/dist/core/src/drift/driftComparator.js.map +1 -0
  96. package/dist/core/src/drift/driftSnapshotLoader.js +136 -0
  97. package/dist/core/src/drift/driftSnapshotLoader.js.map +1 -0
  98. package/dist/core/src/drift/driftSnapshotTypes.js +3 -0
  99. package/dist/core/src/drift/driftSnapshotTypes.js.map +1 -0
  100. package/dist/core/src/drift/driftSnapshotWriter.js +48 -0
  101. package/dist/core/src/drift/driftSnapshotWriter.js.map +1 -0
  102. package/dist/core/src/drift/index.js +6 -0
  103. package/dist/core/src/drift/index.js.map +1 -0
  104. package/dist/core/src/eventArchitectureAnalysis.js +925 -0
  105. package/dist/core/src/eventArchitectureAnalysis.js.map +1 -0
  106. package/dist/core/src/findingTargetResolution.js +117 -0
  107. package/dist/core/src/findingTargetResolution.js.map +1 -0
  108. package/dist/core/src/generateArchitectureDiagrams.js +57 -0
  109. package/dist/core/src/generateArchitectureDiagrams.js.map +1 -0
  110. package/dist/core/src/generateImpactAnalysis.js +366 -0
  111. package/dist/core/src/generateImpactAnalysis.js.map +1 -0
  112. package/dist/core/src/generateOnboardingDocs.js +262 -0
  113. package/dist/core/src/generateOnboardingDocs.js.map +1 -0
  114. package/dist/core/src/governance/cloudConnection.js +203 -0
  115. package/dist/core/src/governance/cloudConnection.js.map +1 -0
  116. package/dist/core/src/governance/governanceContracts.js +129 -0
  117. package/dist/core/src/governance/governanceContracts.js.map +1 -0
  118. package/dist/core/src/governance/governanceGitMetadata.js +78 -0
  119. package/dist/core/src/governance/governanceGitMetadata.js.map +1 -0
  120. package/dist/core/src/governance/governanceUploadClient.js +119 -0
  121. package/dist/core/src/governance/governanceUploadClient.js.map +1 -0
  122. package/dist/core/src/governance/governanceUploadLimitErrorFormatting.js +155 -0
  123. package/dist/core/src/governance/governanceUploadLimitErrorFormatting.js.map +1 -0
  124. package/dist/core/src/governance/governanceUploadPayload.js +439 -0
  125. package/dist/core/src/governance/governanceUploadPayload.js.map +1 -0
  126. package/dist/core/src/governance/runGovernanceUpload.js +103 -0
  127. package/dist/core/src/governance/runGovernanceUpload.js.map +1 -0
  128. package/dist/core/src/impactAnalysisEngine.js +376 -0
  129. package/dist/core/src/impactAnalysisEngine.js.map +1 -0
  130. package/dist/core/src/initializationState.js +99 -0
  131. package/dist/core/src/initializationState.js.map +1 -0
  132. package/dist/core/src/managedArtifacts.js +151 -0
  133. package/dist/core/src/managedArtifacts.js.map +1 -0
  134. package/dist/core/src/moduleContractValidation.js +266 -0
  135. package/dist/core/src/moduleContractValidation.js.map +1 -0
  136. package/dist/core/src/moduleContractsGenerator.js +337 -0
  137. package/dist/core/src/moduleContractsGenerator.js.map +1 -0
  138. package/dist/core/src/moduleDiscovery.js +416 -0
  139. package/dist/core/src/moduleDiscovery.js.map +1 -0
  140. package/dist/core/src/moduleScope.js +426 -0
  141. package/dist/core/src/moduleScope.js.map +1 -0
  142. package/dist/core/src/onboardingGuidance.js +74 -0
  143. package/dist/core/src/onboardingGuidance.js.map +1 -0
  144. package/dist/core/src/operationalLogging.js +7 -0
  145. package/dist/core/src/operationalLogging.js.map +1 -0
  146. package/dist/core/src/packageVersion.js +52 -0
  147. package/dist/core/src/packageVersion.js.map +1 -0
  148. package/dist/core/src/policy/effectivePolicyResolver.js +91 -0
  149. package/dist/core/src/policy/effectivePolicyResolver.js.map +1 -0
  150. package/dist/core/src/policy/exceptionLoader.js +285 -0
  151. package/dist/core/src/policy/exceptionLoader.js.map +1 -0
  152. package/dist/core/src/policy/remotePolicyLoader.js +227 -0
  153. package/dist/core/src/policy/remotePolicyLoader.js.map +1 -0
  154. package/dist/core/src/policy/remotePolicyTypes.js +3 -0
  155. package/dist/core/src/policy/remotePolicyTypes.js.map +1 -0
  156. package/dist/core/src/policy/riskPolicyConfig.js +206 -0
  157. package/dist/core/src/policy/riskPolicyConfig.js.map +1 -0
  158. package/dist/core/src/policy/riskPolicyEnforcement.js +203 -0
  159. package/dist/core/src/policy/riskPolicyEnforcement.js.map +1 -0
  160. package/dist/core/src/policy/riskPolicyExitCode.js +16 -0
  161. package/dist/core/src/policy/riskPolicyExitCode.js.map +1 -0
  162. package/dist/core/src/policy/riskPolicyInitializer.js +90 -0
  163. package/dist/core/src/policy/riskPolicyInitializer.js.map +1 -0
  164. package/dist/core/src/quickFix/quickFixEngine.js +132 -0
  165. package/dist/core/src/quickFix/quickFixEngine.js.map +1 -0
  166. package/dist/core/src/quickFix/quickFixMessaging.js +15 -0
  167. package/dist/core/src/quickFix/quickFixMessaging.js.map +1 -0
  168. package/dist/core/src/quickFix/quickFixProviders.js +87 -0
  169. package/dist/core/src/quickFix/quickFixProviders.js.map +1 -0
  170. package/dist/core/src/quickFix/quickFixTypes.js +3 -0
  171. package/dist/core/src/quickFix/quickFixTypes.js.map +1 -0
  172. package/dist/core/src/remediation/fileHelpers.js +86 -0
  173. package/dist/core/src/remediation/fileHelpers.js.map +1 -0
  174. package/dist/core/src/remediation/fixRegistry.js +645 -0
  175. package/dist/core/src/remediation/fixRegistry.js.map +1 -0
  176. package/dist/core/src/remediation/index.js +8 -0
  177. package/dist/core/src/remediation/index.js.map +1 -0
  178. package/dist/core/src/remediation/remediationEngine.js +207 -0
  179. package/dist/core/src/remediation/remediationEngine.js.map +1 -0
  180. package/dist/core/src/reporting/adrReporter.js +59 -0
  181. package/dist/core/src/reporting/adrReporter.js.map +1 -0
  182. package/dist/core/src/reporting/architectureBaselineReporter.js +64 -0
  183. package/dist/core/src/reporting/architectureBaselineReporter.js.map +1 -0
  184. package/dist/core/src/reporting/architectureDiagramReporter.js +148 -0
  185. package/dist/core/src/reporting/architectureDiagramReporter.js.map +1 -0
  186. package/dist/core/src/reporting/architectureDriftTimelineReporter.js +44 -0
  187. package/dist/core/src/reporting/architectureDriftTimelineReporter.js.map +1 -0
  188. package/dist/core/src/reporting/architectureHealthHistoryReporter.js +51 -0
  189. package/dist/core/src/reporting/architectureHealthHistoryReporter.js.map +1 -0
  190. package/dist/core/src/reporting/architectureMapReporter.js +228 -0
  191. package/dist/core/src/reporting/architectureMapReporter.js.map +1 -0
  192. package/dist/core/src/reporting/architecturePrCommentReporter.js +324 -0
  193. package/dist/core/src/reporting/architecturePrCommentReporter.js.map +1 -0
  194. package/dist/core/src/reporting/architecturePrReviewReporter.js +389 -0
  195. package/dist/core/src/reporting/architecturePrReviewReporter.js.map +1 -0
  196. package/dist/core/src/reporting/architectureReportArtifacts.js +166 -0
  197. package/dist/core/src/reporting/architectureReportArtifacts.js.map +1 -0
  198. package/dist/core/src/reporting/architectureReviewReportGenerator.js +382 -0
  199. package/dist/core/src/reporting/architectureReviewReportGenerator.js.map +1 -0
  200. package/dist/core/src/reporting/architectureReviewReporter.js +1633 -0
  201. package/dist/core/src/reporting/architectureReviewReporter.js.map +1 -0
  202. package/dist/core/src/reporting/architectureRiskAssessment.js +101 -0
  203. package/dist/core/src/reporting/architectureRiskAssessment.js.map +1 -0
  204. package/dist/core/src/reporting/architectureScoreBadge.js +177 -0
  205. package/dist/core/src/reporting/architectureScoreBadge.js.map +1 -0
  206. package/dist/core/src/reporting/architectureSupplementalReporters.js +124 -0
  207. package/dist/core/src/reporting/architectureSupplementalReporters.js.map +1 -0
  208. package/dist/core/src/reporting/changeRiskAssessment.js +6 -0
  209. package/dist/core/src/reporting/changeRiskAssessment.js.map +1 -0
  210. package/dist/core/src/reporting/changeRiskTypes.js +3 -0
  211. package/dist/core/src/reporting/changeRiskTypes.js.map +1 -0
  212. package/dist/core/src/reporting/impactAnalysisReporter.js +111 -0
  213. package/dist/core/src/reporting/impactAnalysisReporter.js.map +1 -0
  214. package/dist/core/src/reporting/onboardingDocsReporter.js +113 -0
  215. package/dist/core/src/reporting/onboardingDocsReporter.js.map +1 -0
  216. package/dist/core/src/reporting/rulePackReporter.js +61 -0
  217. package/dist/core/src/reporting/rulePackReporter.js.map +1 -0
  218. package/dist/core/src/reporting/safetyControlsDetection.js +343 -0
  219. package/dist/core/src/reporting/safetyControlsDetection.js.map +1 -0
  220. package/dist/core/src/resolveRuleAdrs.js +31 -0
  221. package/dist/core/src/resolveRuleAdrs.js.map +1 -0
  222. package/dist/core/src/reviewGenerationWorkflow.js +220 -0
  223. package/dist/core/src/reviewGenerationWorkflow.js.map +1 -0
  224. package/dist/core/src/rulePacks/index.js +17 -0
  225. package/dist/core/src/rulePacks/index.js.map +1 -0
  226. package/dist/core/src/rulePacks/layeredMonolithRulePack.js +45 -0
  227. package/dist/core/src/rulePacks/layeredMonolithRulePack.js.map +1 -0
  228. package/dist/core/src/rulePacks/modularMonolithRulePack.js +41 -0
  229. package/dist/core/src/rulePacks/modularMonolithRulePack.js.map +1 -0
  230. package/dist/core/src/rulePacks/types.js +3 -0
  231. package/dist/core/src/rulePacks/types.js.map +1 -0
  232. package/dist/core/src/setupNextSteps.js +939 -0
  233. package/dist/core/src/setupNextSteps.js.map +1 -0
  234. package/dist/core/src/setupRefresh.js +152 -0
  235. package/dist/core/src/setupRefresh.js.map +1 -0
  236. package/dist/core/src/smartInit/apiDbHintDetection.js +836 -0
  237. package/dist/core/src/smartInit/apiDbHintDetection.js.map +1 -0
  238. package/dist/core/src/smartInit/architectureProposalBuilder.js +1215 -0
  239. package/dist/core/src/smartInit/architectureProposalBuilder.js.map +1 -0
  240. package/dist/core/src/smartInit/architectureStyleDetection.js +472 -0
  241. package/dist/core/src/smartInit/architectureStyleDetection.js.map +1 -0
  242. package/dist/core/src/smartInit/archpilotIgnore.js +139 -0
  243. package/dist/core/src/smartInit/archpilotIgnore.js.map +1 -0
  244. package/dist/core/src/smartInit/componentDetection.js +1084 -0
  245. package/dist/core/src/smartInit/componentDetection.js.map +1 -0
  246. package/dist/core/src/smartInit/implementationProfileResolution.js +53 -0
  247. package/dist/core/src/smartInit/implementationProfileResolution.js.map +1 -0
  248. package/dist/core/src/smartInit/index.js +31 -0
  249. package/dist/core/src/smartInit/index.js.map +1 -0
  250. package/dist/core/src/smartInit/moduleDetection.js +982 -0
  251. package/dist/core/src/smartInit/moduleDetection.js.map +1 -0
  252. package/dist/core/src/smartInit/projectKindDetection.js +437 -0
  253. package/dist/core/src/smartInit/projectKindDetection.js.map +1 -0
  254. package/dist/core/src/smartInit/repoTopologyDetection.js +470 -0
  255. package/dist/core/src/smartInit/repoTopologyDetection.js.map +1 -0
  256. package/dist/core/src/smartInit/runContext.js +112 -0
  257. package/dist/core/src/smartInit/runContext.js.map +1 -0
  258. package/dist/core/src/smartInit/smartInitEngine.js +94 -0
  259. package/dist/core/src/smartInit/smartInitEngine.js.map +1 -0
  260. package/dist/core/src/smartInit/stackDetection.js +503 -0
  261. package/dist/core/src/smartInit/stackDetection.js.map +1 -0
  262. package/dist/core/src/smartInit/toolingHintDetection.js +345 -0
  263. package/dist/core/src/smartInit/toolingHintDetection.js.map +1 -0
  264. package/dist/core/src/sourceScopeClassification.js +83 -0
  265. package/dist/core/src/sourceScopeClassification.js.map +1 -0
  266. package/dist/core/src/stackAdapters.js +2475 -0
  267. package/dist/core/src/stackAdapters.js.map +1 -0
  268. package/dist/core/src/stackMetadata.js +157 -0
  269. package/dist/core/src/stackMetadata.js.map +1 -0
  270. package/dist/core/src/suppressions/index.js +3 -0
  271. package/dist/core/src/suppressions/index.js.map +1 -0
  272. package/dist/core/src/suppressions/suppressionReporter.js +38 -0
  273. package/dist/core/src/suppressions/suppressionReporter.js.map +1 -0
  274. package/dist/core/src/suppressions/suppressionTypes.js +3 -0
  275. package/dist/core/src/suppressions/suppressionTypes.js.map +1 -0
  276. package/dist/core/src/suppressions/suppressionValidator.js +119 -0
  277. package/dist/core/src/suppressions/suppressionValidator.js.map +1 -0
  278. package/dist/core/src/transactionDomainAnalysis.js +1416 -0
  279. package/dist/core/src/transactionDomainAnalysis.js.map +1 -0
  280. package/dist/core/src/validation/changedFilesResolver.js +165 -0
  281. package/dist/core/src/validation/changedFilesResolver.js.map +1 -0
  282. package/dist/core/src/validationProfiling.js +33 -0
  283. package/dist/core/src/validationProfiling.js.map +1 -0
  284. package/dist/core/src/validationRuleExplanations.js +69 -0
  285. package/dist/core/src/validationRuleExplanations.js.map +1 -0
  286. package/dist/core/src/validationSourceInventory.js +200 -0
  287. package/dist/core/src/validationSourceInventory.js.map +1 -0
  288. package/dist/core/src/validationSuppressions.js +231 -0
  289. package/dist/core/src/validationSuppressions.js.map +1 -0
  290. package/dist/core/src/validationTypeScriptAstCache.js +82 -0
  291. package/dist/core/src/validationTypeScriptAstCache.js.map +1 -0
  292. package/dist/core/src/validationWorkflow.js +222 -0
  293. package/dist/core/src/validationWorkflow.js.map +1 -0
  294. package/dist/core/src/workspaceRoot.js +68 -0
  295. package/dist/core/src/workspaceRoot.js.map +1 -0
  296. package/package.json +1 -1
@@ -0,0 +1,1215 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveConcreteBackendStackSelection = resolveConcreteBackendStackSelection;
4
+ exports.buildArchitectureProposal = buildArchitectureProposal;
5
+ const stackAdapters_1 = require("../stackAdapters");
6
+ const implementationProfileResolution_1 = require("./implementationProfileResolution");
7
+ function addEvidence(evidence, value) {
8
+ if (!evidence.includes(value)) {
9
+ evidence.push(value);
10
+ }
11
+ }
12
+ function uniqueSorted(values) {
13
+ return [...new Set(values)].sort((left, right) => left.localeCompare(right));
14
+ }
15
+ function uniqueLimited(values, limit = 5) {
16
+ return uniqueSorted(values.filter((value) => value.trim().length > 0)).slice(0, limit);
17
+ }
18
+ function uniqueInOrder(values) {
19
+ const seen = new Set();
20
+ const result = [];
21
+ for (const value of values) {
22
+ if (seen.has(value)) {
23
+ continue;
24
+ }
25
+ seen.add(value);
26
+ result.push(value);
27
+ }
28
+ return result;
29
+ }
30
+ function uniqueSortedKinds(values) {
31
+ return [...new Set(values)].sort((left, right) => left.localeCompare(right));
32
+ }
33
+ function sortModuleCandidates(candidates) {
34
+ return [...candidates].sort((left, right) => {
35
+ const classificationCompare = left.classification.localeCompare(right.classification);
36
+ if (classificationCompare !== 0) {
37
+ return classificationCompare;
38
+ }
39
+ return left.name.localeCompare(right.name);
40
+ });
41
+ }
42
+ function normalizeIdentity(value) {
43
+ return value
44
+ .toLowerCase()
45
+ .replace(/[^a-z0-9]+/gu, '');
46
+ }
47
+ function normalizeComponentPath(value) {
48
+ return value.replaceAll('\\', '/').replace(/\/+$/gu, '');
49
+ }
50
+ function isComponentAlignedModuleCandidate(candidate, components, resources = []) {
51
+ const candidatePath = normalizeComponentPath(candidate.path);
52
+ const candidateNameIdentity = normalizeIdentity(candidate.name);
53
+ const candidatePathLeafIdentity = normalizeIdentity(candidatePath.split('/').filter((segment) => segment.length > 0).at(-1) ?? candidate.path);
54
+ const matchesComponent = components.some((component) => {
55
+ const componentPath = normalizeComponentPath(component.path);
56
+ if (componentPath.length > 0 &&
57
+ componentPath !== '.' &&
58
+ (componentPath === candidatePath ||
59
+ componentPath.startsWith(`${candidatePath}/`) ||
60
+ candidatePath.startsWith(`${componentPath}/`))) {
61
+ return true;
62
+ }
63
+ const componentIdentities = [
64
+ component.id,
65
+ component.name,
66
+ componentPath.split('/').filter((segment) => segment.length > 0).at(-1) ?? '',
67
+ ].map(normalizeIdentity);
68
+ return (candidateNameIdentity.length > 0 &&
69
+ componentIdentities.some((identity) => identity.length > 0 && (identity === candidateNameIdentity || identity === candidatePathLeafIdentity)));
70
+ });
71
+ if (matchesComponent) {
72
+ return true;
73
+ }
74
+ return resources.some((resource) => {
75
+ const resourcePath = typeof resource.path === 'string' ? normalizeComponentPath(resource.path) : '';
76
+ if (resourcePath.length > 0 &&
77
+ resourcePath !== '.' &&
78
+ (resourcePath === candidatePath ||
79
+ resourcePath.startsWith(`${candidatePath}/`) ||
80
+ candidatePath.startsWith(`${resourcePath}/`))) {
81
+ return true;
82
+ }
83
+ const resourceIdentities = [
84
+ resource.id ?? '',
85
+ resource.name ?? '',
86
+ resource.provider ?? '',
87
+ resourcePath.split('/').filter((segment) => segment.length > 0).at(-1) ?? '',
88
+ ].map(normalizeIdentity);
89
+ return (candidateNameIdentity.length > 0 &&
90
+ resourceIdentities.some((identity) => identity.length > 0 && (identity === candidateNameIdentity || identity === candidatePathLeafIdentity)));
91
+ });
92
+ }
93
+ function findSupportOwnerComponent(candidate, components) {
94
+ const candidatePath = normalizeComponentPath(candidate.path);
95
+ return components
96
+ .filter((component) => component.selectedByDefault === false || component.governanceRole === 'support')
97
+ .find((component) => {
98
+ const componentPath = normalizeComponentPath(component.path);
99
+ return componentPath.length > 0 &&
100
+ componentPath !== '.' &&
101
+ (candidatePath === componentPath || candidatePath.startsWith(`${componentPath}/`));
102
+ });
103
+ }
104
+ function resolveModuleCandidates(detection, resources = []) {
105
+ return sortModuleCandidates((detection.modules?.modules ?? []).map((module) => {
106
+ const isComponentOrResourceAligned = isComponentAlignedModuleCandidate(module, detection.detectedComponents ?? [], resources);
107
+ const supportOwner = findSupportOwnerComponent(module, detection.detectedComponents ?? []);
108
+ const isSupportOwned = Boolean(supportOwner);
109
+ return {
110
+ name: module.name,
111
+ path: module.path,
112
+ classification: (isComponentOrResourceAligned || isSupportOwned) && module.classification === 'architecture-module'
113
+ ? 'uncertain'
114
+ : module.classification,
115
+ confidence: isSupportOwned && module.confidence === 'high' ? 'medium' : module.confidence,
116
+ evidence: [
117
+ ...module.evidence,
118
+ ...(isComponentOrResourceAligned
119
+ ? ['Matches detected component/service/app or resource identity; review before governing as a module']
120
+ : []),
121
+ ...(supportOwner
122
+ ? [`Located under support/tooling component ${supportOwner.path}; available for manual inclusion`]
123
+ : []),
124
+ ],
125
+ selectedByDefault: (isComponentOrResourceAligned || isSupportOwned) && module.classification === 'architecture-module'
126
+ ? false
127
+ : module.selectedByDefault,
128
+ };
129
+ }));
130
+ }
131
+ function groupModuleCandidates(candidates) {
132
+ if (candidates.length === 0) {
133
+ return undefined;
134
+ }
135
+ return {
136
+ 'architecture-module': candidates.filter((candidate) => candidate.classification === 'architecture-module'),
137
+ 'technical-layer': candidates.filter((candidate) => candidate.classification === 'technical-layer'),
138
+ 'shared-infrastructure': candidates.filter((candidate) => candidate.classification === 'shared-infrastructure'),
139
+ uncertain: candidates.filter((candidate) => candidate.classification === 'uncertain'),
140
+ };
141
+ }
142
+ function isProposalProjectKind(value) {
143
+ return value === 'backend' || value === 'frontend' || value === 'library';
144
+ }
145
+ function getSelectedCandidate(detection) {
146
+ return detection.stacks.candidates?.find((candidate) => candidate.adapterId === detection.stacks.selectedAdapterId);
147
+ }
148
+ function getBackendComponents(detection) {
149
+ return (detection.detectedComponents ?? [])
150
+ .filter((component) => component.kind === 'backend' || component.kind === 'worker');
151
+ }
152
+ function getDefaultGovernedBackendComponents(detection) {
153
+ const components = detection.detectedComponents ?? [];
154
+ return components
155
+ .filter((component) => component.kind === 'backend' || component.kind === 'worker')
156
+ .filter((component) => isDefaultGovernedComponent(component, components));
157
+ }
158
+ function getFrontendComponents(detection) {
159
+ return (detection.detectedComponents ?? [])
160
+ .filter((component) => component.kind === 'frontend');
161
+ }
162
+ function getLibraryComponents(detection) {
163
+ return (detection.detectedComponents ?? [])
164
+ .filter((component) => component.kind === 'library');
165
+ }
166
+ function getDataComponents(detection) {
167
+ return (detection.detectedComponents ?? [])
168
+ .filter((component) => component.kind === 'data');
169
+ }
170
+ function isArchitectureComponentKind(kind) {
171
+ return kind === 'backend' ||
172
+ kind === 'frontend' ||
173
+ kind === 'worker' ||
174
+ kind === 'library' ||
175
+ kind === 'infrastructure' ||
176
+ kind === 'data';
177
+ }
178
+ function componentEvidenceText(component) {
179
+ return [
180
+ component.name,
181
+ component.path,
182
+ ...(component.governanceEvidence ?? []),
183
+ ].join(' ').toLowerCase();
184
+ }
185
+ function isCliSupportCandidate(component, allComponents) {
186
+ const text = componentEvidenceText(component);
187
+ if (!/\b(project kind cli|cli\/tooling project kind|bin|commander|yargs|ts-morph|schematics|scaffold|generator)\b/u.test(text)) {
188
+ return false;
189
+ }
190
+ const hasRuntimePeer = allComponents.some((peer) => {
191
+ if (peer.path === component.path || peer.selectedByDefault === false) {
192
+ return false;
193
+ }
194
+ return peer.kind === 'backend' ||
195
+ peer.kind === 'frontend' ||
196
+ peer.kind === 'worker' ||
197
+ Boolean(peer.apiStyle && peer.apiStyle !== 'none' && peer.apiStyle !== 'unknown');
198
+ });
199
+ return hasRuntimePeer;
200
+ }
201
+ function hasIndependentRuntimeEvidence(component) {
202
+ const text = componentEvidenceText(component);
203
+ return component.governanceRole === 'runtime' && (/Maven executable application evidence/iu.test(text) ||
204
+ /Maven deployable WAR artifact/iu.test(text) ||
205
+ /Published package surface signal/iu.test(text) && !/Path contains support\/tooling role tokens/iu.test(text));
206
+ }
207
+ function hasSupportRoleEvidence(component) {
208
+ const text = componentEvidenceText(component);
209
+ return /Path contains support\/tooling role tokens|Package metadata describes test\/support usage|Package scripts include development\/test\/support workflows|Private package without runtime publish surface/iu.test(text);
210
+ }
211
+ function isDefaultGovernedComponent(component, allComponents) {
212
+ if (component.selectedByDefault === false || component.governanceRole === 'support') {
213
+ return false;
214
+ }
215
+ const componentPath = normalizeComponentPath(component.path);
216
+ const supportParent = allComponents.some((peer) => {
217
+ if (peer.path === component.path || !(peer.selectedByDefault === false || peer.governanceRole === 'support')) {
218
+ return false;
219
+ }
220
+ const peerPath = normalizeComponentPath(peer.path);
221
+ return peerPath.length > 0 && peerPath !== '.' && componentPath.startsWith(`${peerPath}/`);
222
+ });
223
+ if (supportParent && !hasIndependentRuntimeEvidence(component)) {
224
+ return false;
225
+ }
226
+ if (isCliSupportCandidate(component, allComponents)) {
227
+ return false;
228
+ }
229
+ return true;
230
+ }
231
+ function toComponentProjectKind(kind) {
232
+ if (kind === 'backend' || kind === 'worker') {
233
+ return 'backend';
234
+ }
235
+ if (kind === 'frontend') {
236
+ return 'frontend';
237
+ }
238
+ if (kind === 'library') {
239
+ return 'library';
240
+ }
241
+ return undefined;
242
+ }
243
+ function toComponentType(kind) {
244
+ switch (kind) {
245
+ case 'frontend':
246
+ return 'frontend_app';
247
+ case 'backend':
248
+ return 'backend_service';
249
+ case 'worker':
250
+ return 'worker';
251
+ case 'library':
252
+ return 'library';
253
+ case 'infrastructure':
254
+ return 'infrastructure';
255
+ case 'data':
256
+ return 'data';
257
+ default:
258
+ return 'component';
259
+ }
260
+ }
261
+ function toComponentStacks(component) {
262
+ if (!component.stack) {
263
+ return undefined;
264
+ }
265
+ if (component.kind === 'backend' || component.kind === 'worker') {
266
+ return { backendStack: component.stack };
267
+ }
268
+ if (component.kind === 'frontend') {
269
+ return { frontendStack: component.stack };
270
+ }
271
+ if (component.kind === 'library') {
272
+ return { libraryStack: component.stack };
273
+ }
274
+ return undefined;
275
+ }
276
+ function resolveComponentModulesRoot(component, moduleRoots) {
277
+ if (component.path === '.') {
278
+ return moduleRoots.find((root) => root !== '.' && !root.includes('/')) ?? moduleRoots[0];
279
+ }
280
+ const componentPrefix = `${component.path}/`;
281
+ return moduleRoots.find((root) => root === component.path || root.startsWith(componentPrefix));
282
+ }
283
+ function resolveProposalComponents(detection) {
284
+ if (detection.topology.topology !== 'monorepo' &&
285
+ (detection.detectedComponents?.length ?? 0) <= 1) {
286
+ return undefined;
287
+ }
288
+ const components = (detection.detectedComponents ?? [])
289
+ .filter((component) => isArchitectureComponentKind(component.kind))
290
+ .filter((component, _index, allComponents) => isDefaultGovernedComponent(component, allComponents))
291
+ .map((component) => {
292
+ const projectKind = toComponentProjectKind(component.kind);
293
+ const stacks = toComponentStacks(component);
294
+ const componentModulesRoot = resolveComponentModulesRoot(component, detection.modules?.moduleRoots ?? []);
295
+ return {
296
+ id: component.id,
297
+ name: component.name,
298
+ path: component.path,
299
+ type: toComponentType(component.kind),
300
+ kind: component.kind,
301
+ ...(component.stack ? { stack: component.stack } : {}),
302
+ ...(projectKind ? { projectKinds: [projectKind] } : {}),
303
+ ...(stacks ? { stacks } : {}),
304
+ ...(componentModulesRoot ? { structure: { modulesRoot: componentModulesRoot } } : {}),
305
+ ...(component.apiStyle && component.apiStyle !== 'unknown'
306
+ ? { apiStyle: component.apiStyle === 'none' ? 'none' : component.apiStyle }
307
+ : {}),
308
+ ...(component.database && component.database !== 'unknown'
309
+ ? {
310
+ database: component.database === 'postgresql' || component.database === 'mysql'
311
+ ? component.database
312
+ : 'none',
313
+ }
314
+ : {}),
315
+ };
316
+ });
317
+ return components.length > 0 ? components : undefined;
318
+ }
319
+ function resolveResourceOwnerComponent(detection) {
320
+ const backendComponents = getDefaultGovernedBackendComponents(detection);
321
+ return backendComponents.find((component) => component.kind === 'backend') ??
322
+ backendComponents[0];
323
+ }
324
+ function resourceIdFor(ownerId, suffix) {
325
+ if (suffix === 'api' && /(^|-)api$/u.test(ownerId)) {
326
+ return ownerId;
327
+ }
328
+ if (suffix === 'database') {
329
+ return `${ownerId}-db`
330
+ .replace(/[^a-z0-9_-]+/giu, '-')
331
+ .replace(/^-+|-+$/gu, '')
332
+ .toLowerCase();
333
+ }
334
+ return `${ownerId}-${suffix}`
335
+ .replace(/[^a-z0-9_-]+/giu, '-')
336
+ .replace(/^-+|-+$/gu, '')
337
+ .toLowerCase();
338
+ }
339
+ function readableResourceName(componentName, suffix, provider) {
340
+ const unscopedName = componentName.includes('/')
341
+ ? componentName.slice(componentName.lastIndexOf('/') + 1)
342
+ : componentName;
343
+ const cleanName = unscopedName
344
+ .replace(/^@/u, '')
345
+ .replace(/[-_]+/gu, ' ')
346
+ .trim();
347
+ if (suffix === 'api') {
348
+ return /\bapi$/iu.test(cleanName) ? cleanName.toUpperCase() : `${cleanName} API`;
349
+ }
350
+ if (provider === 'postgresql') {
351
+ return 'Postgres';
352
+ }
353
+ return /\b(db|database)$/iu.test(cleanName) ? cleanName : `${cleanName} Database`;
354
+ }
355
+ function resolveProposalResources(input) {
356
+ if (input.detection.topology.topology !== 'monorepo' &&
357
+ (input.detection.detectedComponents?.length ?? 0) <= 1) {
358
+ return undefined;
359
+ }
360
+ const resources = [];
361
+ const usedIds = new Set();
362
+ const addResource = (resource) => {
363
+ if (!resource.id || usedIds.has(resource.id)) {
364
+ return;
365
+ }
366
+ usedIds.add(resource.id);
367
+ resources.push(resource);
368
+ };
369
+ for (const component of getDefaultGovernedBackendComponents(input.detection)) {
370
+ if (component.apiStyle && component.apiStyle !== 'unknown' && component.apiStyle !== 'none') {
371
+ addResource({
372
+ id: resourceIdFor(component.id, 'api'),
373
+ name: readableResourceName(component.name, 'api', component.apiStyle),
374
+ type: 'api',
375
+ provider: component.apiStyle,
376
+ ownerComponent: component.id,
377
+ component: component.id,
378
+ components: [component.id],
379
+ });
380
+ }
381
+ if (component.database && component.database !== 'unknown' && component.database !== 'none') {
382
+ addResource({
383
+ id: component.database === 'postgresql' ? 'postgres' : resourceIdFor(component.id, 'database'),
384
+ name: readableResourceName(component.name, 'database', component.database),
385
+ type: 'database',
386
+ provider: component.database,
387
+ ownerComponent: component.id,
388
+ component: component.id,
389
+ components: [component.id],
390
+ });
391
+ }
392
+ }
393
+ for (const component of getDataComponents(input.detection)) {
394
+ if (component.database && component.database !== 'unknown' && component.database !== 'none') {
395
+ addResource({
396
+ id: component.database === 'postgresql' ? 'postgres' : resourceIdFor(component.id, 'database'),
397
+ name: readableResourceName(component.name, 'database', component.database),
398
+ type: 'database',
399
+ provider: component.database,
400
+ ownerComponent: component.id,
401
+ component: component.id,
402
+ components: [component.id],
403
+ });
404
+ }
405
+ }
406
+ const owner = resolveResourceOwnerComponent(input.detection);
407
+ const detectedDatabase = input.database;
408
+ const hasDatabaseResource = resources.some((resource) => resource.type === 'database');
409
+ if (owner &&
410
+ !hasDatabaseResource &&
411
+ detectedDatabase &&
412
+ detectedDatabase !== 'unknown' &&
413
+ detectedDatabase !== 'none') {
414
+ addResource({
415
+ id: detectedDatabase === 'postgresql' ? 'postgres' : resourceIdFor(owner.id, 'database'),
416
+ name: readableResourceName(owner.name, 'database', detectedDatabase),
417
+ type: 'database',
418
+ provider: detectedDatabase,
419
+ ownerComponent: owner.id,
420
+ component: owner.id,
421
+ components: [owner.id],
422
+ });
423
+ }
424
+ const hasApiResource = resources.some((resource) => resource.type === 'api');
425
+ if (owner &&
426
+ !hasApiResource &&
427
+ input.apiStyle &&
428
+ input.apiStyle !== 'unknown' &&
429
+ input.apiStyle !== 'none') {
430
+ addResource({
431
+ id: resourceIdFor(owner.id, 'api'),
432
+ name: readableResourceName(owner.name, 'api', input.apiStyle),
433
+ type: 'api',
434
+ provider: input.apiStyle,
435
+ ownerComponent: owner.id,
436
+ component: owner.id,
437
+ components: [owner.id],
438
+ });
439
+ }
440
+ return resources.length > 0
441
+ ? resources.sort((left, right) => (left.id ?? '').localeCompare(right.id ?? ''))
442
+ : undefined;
443
+ }
444
+ function hasAttributableBackendRoot(detection) {
445
+ return getBackendComponents(detection).length > 0;
446
+ }
447
+ function isGenericLibraryStack(stack) {
448
+ return stack === 'node_typescript' ||
449
+ stack === 'node_javascript' ||
450
+ stack === 'python' ||
451
+ stack === 'go' ||
452
+ stack === 'kotlin' ||
453
+ stack === 'terraform' ||
454
+ stack === 'ansible';
455
+ }
456
+ function hasProjectKindEvidence(detection, patterns) {
457
+ const evidence = detection.projectKinds?.evidence ?? [];
458
+ return evidence.some((entry) => patterns.some((pattern) => pattern.test(entry)));
459
+ }
460
+ function resolveSingleProjectGenericKinds(detection, selectedAdapterId, selectedHints, evidence) {
461
+ if (detection.topology.topology !== 'single_project' ||
462
+ !selectedAdapterId ||
463
+ (0, stackAdapters_1.getStackAdapterById)(selectedAdapterId).category !== 'generic-language' ||
464
+ !(selectedHints.includes('backend') && selectedHints.includes('library'))) {
465
+ return undefined;
466
+ }
467
+ const hasFrameworkBackendCandidate = (detection.stacks.candidates ?? []).some((candidate) => {
468
+ const adapter = (0, stackAdapters_1.getStackAdapterById)(candidate.adapterId);
469
+ return adapter.category === 'framework' && adapter.id !== selectedAdapterId;
470
+ });
471
+ if (hasFrameworkBackendCandidate) {
472
+ return undefined;
473
+ }
474
+ const hasServiceBackendEvidence = hasProjectKindEvidence(detection, [
475
+ /requirements\.txt/u,
476
+ /composer\.json/u,
477
+ /pom\.xml/u,
478
+ /build\.gradle/u,
479
+ /openapi/u,
480
+ /db\/ or migrations/u,
481
+ /repository-layer/u,
482
+ ]);
483
+ const hasCliEntrypointEvidence = hasProjectKindEvidence(detection, [
484
+ /cmd\/ entrypoint/u,
485
+ /CLI-style entrypoint/u,
486
+ ]);
487
+ const hasLibraryEvidence = hasProjectKindEvidence(detection, [
488
+ /exports field/u,
489
+ /main\/module\/types fields/u,
490
+ /src\/index/u,
491
+ /pyproject\.toml/u,
492
+ /setup\.py/u,
493
+ /go\.mod/u,
494
+ /Python package __init__\.py/u,
495
+ /reusable module folders/u,
496
+ ]);
497
+ const selected = hasLibraryEvidence && !hasServiceBackendEvidence ? 'library' : 'backend';
498
+ addEvidence(evidence, `Reduced single-project generic ${selectedAdapterId} project kind to ${selected}`);
499
+ if (selected === 'library' && hasCliEntrypointEvidence) {
500
+ addEvidence(evidence, 'Treated CLI entrypoint as package tooling rather than backend service evidence');
501
+ }
502
+ return [selected];
503
+ }
504
+ function normalizeDetectedBackendStack(value) {
505
+ if (value === 'springboot') {
506
+ return 'spring';
507
+ }
508
+ if (typeof value !== 'string') {
509
+ return undefined;
510
+ }
511
+ const adapter = (0, stackAdapters_1.getStackAdapterById)(value);
512
+ const backendStacks = new Set([
513
+ 'node_typescript',
514
+ 'express',
515
+ 'nestjs',
516
+ 'java',
517
+ 'spring',
518
+ 'fastapi',
519
+ 'django',
520
+ 'flask',
521
+ 'python',
522
+ 'php',
523
+ 'laravel',
524
+ 'go',
525
+ 'dotnet',
526
+ 'ruby_rails',
527
+ 'kotlin',
528
+ 'kotlin_spring',
529
+ 'rust',
530
+ 'other',
531
+ ]);
532
+ return backendStacks.has(adapter.id) && adapter.id !== 'other'
533
+ ? adapter.id
534
+ : undefined;
535
+ }
536
+ function resolveConcreteBackendStackSelection(stacks) {
537
+ const candidates = uniqueInOrder(stacks
538
+ .map((stack) => normalizeDetectedBackendStack(stack))
539
+ .filter((stack) => Boolean(stack)));
540
+ if (candidates.length === 0) {
541
+ return { candidates, ambiguous: false, reason: 'none' };
542
+ }
543
+ if (candidates.length === 1) {
544
+ return { selected: candidates[0], candidates, ambiguous: false, reason: 'single' };
545
+ }
546
+ const frameworkCandidates = candidates.filter((stack) => (0, stackAdapters_1.getStackAdapterById)(stack).category === 'framework');
547
+ if (frameworkCandidates.length === 1) {
548
+ return {
549
+ selected: frameworkCandidates[0],
550
+ candidates,
551
+ ambiguous: false,
552
+ reason: 'framework-preferred',
553
+ };
554
+ }
555
+ return { candidates, ambiguous: true, reason: 'ambiguous' };
556
+ }
557
+ function compatibleGenericBackendStacks(stack) {
558
+ switch (stack) {
559
+ case 'express':
560
+ case 'nestjs':
561
+ return ['node_typescript', 'node_javascript'];
562
+ case 'spring':
563
+ return ['java'];
564
+ case 'fastapi':
565
+ case 'django':
566
+ case 'flask':
567
+ return ['python'];
568
+ case 'laravel':
569
+ return ['php'];
570
+ case 'kotlin_spring':
571
+ return ['kotlin'];
572
+ default:
573
+ return [];
574
+ }
575
+ }
576
+ function isFrameworkBackendStack(stack) {
577
+ return (0, stackAdapters_1.getStackAdapterById)(stack).category === 'framework';
578
+ }
579
+ function getBackendCandidateByStack(detection, stack) {
580
+ return detection.stacks.candidates?.find((candidate) => normalizeDetectedBackendStack(candidate.adapterId) === stack);
581
+ }
582
+ function isLikelyFrontendNodeToolingStack(detection, stack, selectedStack) {
583
+ return (stack === 'node_typescript' || stack === 'node_javascript') &&
584
+ selectedStack !== 'node_typescript' &&
585
+ selectedStack !== 'node_javascript' &&
586
+ !getBackendComponents(detection).some((component) => component.stack === stack &&
587
+ component.confidence === 'high' &&
588
+ component.governanceRole === 'runtime' &&
589
+ isDefaultGovernedComponent(component, detection.detectedComponents ?? []));
590
+ }
591
+ function isCredibleCompetingSingleProjectBackendCandidate(detection, stack, selectedStack) {
592
+ if (compatibleGenericBackendStacks(selectedStack).includes(stack)) {
593
+ return false;
594
+ }
595
+ if (isLikelyFrontendNodeToolingStack(detection, stack, selectedStack)) {
596
+ return false;
597
+ }
598
+ const candidate = getBackendCandidateByStack(detection, stack);
599
+ if (!candidate || candidate.confidence !== 'high') {
600
+ return false;
601
+ }
602
+ return true;
603
+ }
604
+ function resolveSingleProjectBackendStackFromRuntimeComponent(detection, backendComponents, detectedBackendStacks) {
605
+ if (detection.topology.topology !== 'single_project') {
606
+ return undefined;
607
+ }
608
+ const allComponents = detection.detectedComponents ?? [];
609
+ const runtimeBackendComponents = backendComponents
610
+ .filter((component) => component.confidence === 'high' &&
611
+ component.governanceRole === 'runtime' &&
612
+ isDefaultGovernedComponent(component, allComponents));
613
+ const runtimeBackendStacks = uniqueInOrder(runtimeBackendComponents
614
+ .map((component) => normalizeDetectedBackendStack(component.stack))
615
+ .filter((stack) => Boolean(stack)));
616
+ if (runtimeBackendStacks.length !== 1) {
617
+ return undefined;
618
+ }
619
+ const selectedStack = normalizeDetectedBackendStack(detection.stacks.selectedAdapterId);
620
+ const componentStack = runtimeBackendStacks[0];
621
+ if (selectedStack !== componentStack) {
622
+ return undefined;
623
+ }
624
+ const competingCandidates = uniqueInOrder(detectedBackendStacks)
625
+ .filter((stack) => stack !== componentStack)
626
+ .filter((stack) => isCredibleCompetingSingleProjectBackendCandidate(detection, stack, componentStack));
627
+ return competingCandidates.length === 0 ? componentStack : undefined;
628
+ }
629
+ function backendComponentSignificance(component) {
630
+ let score = 1;
631
+ if (component.governanceRole === 'runtime') {
632
+ score += 1;
633
+ }
634
+ if (component.confidence === 'high') {
635
+ score += 1;
636
+ }
637
+ else if (component.confidence === 'medium') {
638
+ score += 0.5;
639
+ }
640
+ if (component.apiStyle && component.apiStyle !== 'none' && component.apiStyle !== 'unknown') {
641
+ score += 2;
642
+ }
643
+ if ((component.evidence ?? []).some((entry) => /architecture root signal:\s*backend_app/iu.test(entry))) {
644
+ score += 1;
645
+ }
646
+ return score;
647
+ }
648
+ function resolveDominantBackendFrameworkFromComponents(components) {
649
+ const relevantBackendComponents = components
650
+ .filter((component) => component.kind === 'backend' || component.kind === 'worker')
651
+ .filter((component, _index, allComponents) => isDefaultGovernedComponent(component, allComponents))
652
+ .filter((component) => !hasSupportRoleEvidence(component) || hasIndependentRuntimeEvidence(component))
653
+ .filter((component) => component.governanceRole !== 'support' && Boolean(component.stack));
654
+ const candidates = uniqueInOrder(relevantBackendComponents
655
+ .map((component) => normalizeDetectedBackendStack(component.stack))
656
+ .filter((stack) => Boolean(stack)));
657
+ if (candidates.length === 0) {
658
+ return undefined;
659
+ }
660
+ const frameworkScores = new Map();
661
+ const genericStacks = new Set();
662
+ for (const component of relevantBackendComponents) {
663
+ const stack = normalizeDetectedBackendStack(component.stack);
664
+ if (!stack) {
665
+ continue;
666
+ }
667
+ if (isFrameworkBackendStack(stack)) {
668
+ frameworkScores.set(stack, (frameworkScores.get(stack) ?? 0) + backendComponentSignificance(component));
669
+ }
670
+ else {
671
+ genericStacks.add(stack);
672
+ }
673
+ }
674
+ const frameworkEntries = [...frameworkScores.entries()]
675
+ .sort((left, right) => {
676
+ const scoreDiff = right[1] - left[1];
677
+ return scoreDiff !== 0 ? scoreDiff : left[0].localeCompare(right[0]);
678
+ });
679
+ if (frameworkEntries.length === 0) {
680
+ return resolveConcreteBackendStackSelection(candidates);
681
+ }
682
+ if (frameworkEntries.length === 1) {
683
+ const selected = frameworkEntries[0][0];
684
+ return {
685
+ selected,
686
+ candidates,
687
+ ambiguous: false,
688
+ reason: 'framework-preferred',
689
+ };
690
+ }
691
+ const [topStack, topScore] = frameworkEntries[0];
692
+ const [, nextScore] = frameworkEntries[1];
693
+ const compatibleGenericStacks = new Set(compatibleGenericBackendStacks(topStack));
694
+ const incompatibleGenericStacks = [...genericStacks].filter((stack) => !compatibleGenericStacks.has(stack));
695
+ if (topScore >= nextScore * 2 && incompatibleGenericStacks.length === 0) {
696
+ return {
697
+ selected: topStack,
698
+ candidates,
699
+ ambiguous: false,
700
+ reason: 'dominant-framework',
701
+ };
702
+ }
703
+ return {
704
+ candidates,
705
+ ambiguous: true,
706
+ reason: 'ambiguous',
707
+ };
708
+ }
709
+ function resolveNormalizedProjectKinds(detection, evidence) {
710
+ const heuristicKinds = (detection.projectKinds?.kinds ?? []).filter(isProposalProjectKind);
711
+ if (detection.topology.topology === 'monorepo') {
712
+ const componentKinds = [
713
+ ...(getBackendComponents(detection).length > 0 ? ['backend'] : []),
714
+ ...(getFrontendComponents(detection).length > 0 ? ['frontend'] : []),
715
+ ...(getLibraryComponents(detection).length > 0 || getDataComponents(detection).length > 0 ? ['library'] : []),
716
+ ];
717
+ if (componentKinds.length > 0) {
718
+ addEvidence(evidence, 'Normalized monorepo project kinds from detected component roots');
719
+ return uniqueSortedKinds(componentKinds);
720
+ }
721
+ }
722
+ const selectedAdapterId = detection.stacks.selectedAdapterId;
723
+ const selectedCandidate = getSelectedCandidate(detection);
724
+ const selectedAdapter = (0, stackAdapters_1.getStackAdapterById)(selectedAdapterId);
725
+ const selectedHints = selectedCandidate?.projectKindHints ??
726
+ (selectedAdapterId
727
+ ? (0, stackAdapters_1.getStackAdapterById)(selectedAdapterId).resolveProjectKinds?.(selectedCandidate ?? {
728
+ adapterId: selectedAdapterId,
729
+ confidence: 'low',
730
+ score: 0,
731
+ evidence: [],
732
+ }) ?? []
733
+ : []);
734
+ let normalizedKinds = [];
735
+ const genericSingleProjectKinds = resolveSingleProjectGenericKinds(detection, selectedAdapterId, selectedHints, evidence);
736
+ if (genericSingleProjectKinds) {
737
+ normalizedKinds = genericSingleProjectKinds;
738
+ }
739
+ else if (detection.topology.topology === 'single_project' && selectedHints.length > 0 && selectedAdapter.category === 'framework') {
740
+ normalizedKinds = uniqueSortedKinds(selectedHints);
741
+ addEvidence(evidence, `Normalized project kinds from selected adapter '${selectedAdapter.id}' hints`);
742
+ }
743
+ else if (detection.topology.topology === 'monorepo' && selectedHints.length > 0) {
744
+ normalizedKinds = uniqueSortedKinds([...heuristicKinds, ...selectedHints]);
745
+ if (normalizedKinds.includes('library') &&
746
+ !selectedHints.includes('library') &&
747
+ (detection.architectureStyleSignals?.sharedPackageCount ?? 0) === 0) {
748
+ normalizedKinds = normalizedKinds.filter((kind) => kind !== 'library');
749
+ }
750
+ addEvidence(evidence, 'Normalized monorepo project kinds from adapter hints plus heuristic evidence');
751
+ }
752
+ else if (heuristicKinds.length > 0) {
753
+ normalizedKinds = uniqueSortedKinds(heuristicKinds);
754
+ }
755
+ else if (selectedHints.length > 0) {
756
+ normalizedKinds = uniqueSortedKinds(selectedHints);
757
+ addEvidence(evidence, `Used selected adapter '${selectedAdapter.id}' hints as proposal project kinds fallback`);
758
+ }
759
+ if (normalizedKinds.length === 0 && detection.stacks.frontendStacks.length > 0) {
760
+ normalizedKinds = ['frontend'];
761
+ addEvidence(evidence, 'Normalized project kinds from detected frontend stack evidence');
762
+ }
763
+ else if (normalizedKinds.length === 0 && detection.stacks.backendStacks.length > 0) {
764
+ normalizedKinds = ['backend'];
765
+ addEvidence(evidence, 'Normalized project kinds from detected backend stack evidence');
766
+ }
767
+ return normalizedKinds;
768
+ }
769
+ function resolveArchitectureStyle(detection, projectKinds, evidence) {
770
+ const architectureStyleEvidence = [];
771
+ const topology = detection.topology.topology;
772
+ const modulesRoot = detection.modules?.modulesRoot;
773
+ const moduleCount = detection.modules?.modules.length ?? 0;
774
+ const architectureSignals = detection.architectureStyleSignals;
775
+ const backendAppCount = architectureSignals?.backendAppCount ?? (projectKinds.includes('backend') ? 1 : 0);
776
+ const frontendAppCount = architectureSignals?.frontendAppCount ?? (projectKinds.includes('frontend') ? 1 : 0);
777
+ const sharedPackageCount = architectureSignals?.sharedPackageCount ?? 0;
778
+ const strongMicroserviceCount = architectureSignals?.strongMicroserviceCount ?? 0;
779
+ const serviceCommunicationCount = architectureSignals?.serviceCommunicationCount ?? 0;
780
+ const deploymentConfiguredServiceCount = architectureSignals?.deploymentConfiguredServiceCount ?? 0;
781
+ const envConfiguredServiceCount = architectureSignals?.envConfiguredServiceCount ?? 0;
782
+ const openApiServiceCount = architectureSignals?.openApiServiceCount ?? 0;
783
+ if (topology === 'monorepo') {
784
+ addEvidence(architectureStyleEvidence, 'Detected monorepo topology');
785
+ }
786
+ if (backendAppCount === 1) {
787
+ addEvidence(architectureStyleEvidence, 'Detected single backend app');
788
+ }
789
+ else if (backendAppCount > 1) {
790
+ addEvidence(architectureStyleEvidence, `Detected ${backendAppCount} backend app roots`);
791
+ }
792
+ if (frontendAppCount > 0) {
793
+ addEvidence(architectureStyleEvidence, `Detected ${frontendAppCount} frontend app root${frontendAppCount === 1 ? '' : 's'}`);
794
+ }
795
+ if (sharedPackageCount > 0) {
796
+ addEvidence(architectureStyleEvidence, `Detected shared packages (${sharedPackageCount})`);
797
+ }
798
+ if (modulesRoot) {
799
+ addEvidence(architectureStyleEvidence, `Detected modules root at ${modulesRoot}`);
800
+ }
801
+ else if (moduleCount > 0) {
802
+ addEvidence(architectureStyleEvidence, `Detected ${moduleCount} module candidates`);
803
+ }
804
+ const hasStrongMicroserviceSignals = strongMicroserviceCount >= 2 &&
805
+ (serviceCommunicationCount >= 1 || deploymentConfiguredServiceCount >= 2 || envConfiguredServiceCount >= 2 || openApiServiceCount >= 2);
806
+ const hasMultipleComponentWorkspace = topology === 'monorepo' &&
807
+ (backendAppCount + frontendAppCount + sharedPackageCount >= 2 ||
808
+ (detection.detectedComponents ?? []).filter((component) => component.path !== '.').length >= 2);
809
+ if (hasStrongMicroserviceSignals) {
810
+ addEvidence(architectureStyleEvidence, `Detected ${strongMicroserviceCount} independent backend services with API, DB, and service-level operational config`);
811
+ if (serviceCommunicationCount > 0) {
812
+ addEvidence(architectureStyleEvidence, 'Detected service-to-service communication');
813
+ }
814
+ addEvidence(architectureStyleEvidence, 'Proposed Style: microservices');
815
+ for (const item of architectureStyleEvidence) {
816
+ addEvidence(evidence, item);
817
+ }
818
+ return {
819
+ architectureStyle: 'microservices',
820
+ architectureStyleEvidence,
821
+ };
822
+ }
823
+ if (hasMultipleComponentWorkspace && !(backendAppCount === 1 && modulesRoot && frontendAppCount === 0 && sharedPackageCount === 0)) {
824
+ addEvidence(architectureStyleEvidence, 'Detected multi-component monorepo workspace');
825
+ addEvidence(architectureStyleEvidence, 'Proposed Style: multi_component_monorepo');
826
+ for (const item of architectureStyleEvidence) {
827
+ addEvidence(evidence, item);
828
+ }
829
+ return {
830
+ architectureStyle: 'multi_component_monorepo',
831
+ architectureStyleEvidence,
832
+ };
833
+ }
834
+ if (backendAppCount === 1 && modulesRoot) {
835
+ addEvidence(architectureStyleEvidence, 'Single backend app plus module boundaries favor a modular monolith');
836
+ }
837
+ else if (topology === 'monorepo' && backendAppCount === 1 && (frontendAppCount > 0 || sharedPackageCount > 0)) {
838
+ addEvidence(architectureStyleEvidence, 'Monorepo contains one backend app plus frontend/shared packages');
839
+ }
840
+ else if (backendAppCount <= 1 && projectKinds.includes('backend')) {
841
+ addEvidence(architectureStyleEvidence, 'No independent service boundaries detected');
842
+ }
843
+ else if (backendAppCount > 1) {
844
+ addEvidence(architectureStyleEvidence, 'Multiple backend roots detected, but independent microservice boundaries were not strong enough');
845
+ }
846
+ else {
847
+ return {
848
+ architectureStyleEvidence,
849
+ };
850
+ }
851
+ addEvidence(architectureStyleEvidence, 'Proposed Style: modular_monolith');
852
+ for (const item of architectureStyleEvidence) {
853
+ addEvidence(evidence, item);
854
+ }
855
+ return {
856
+ architectureStyle: 'modular_monolith',
857
+ architectureStyleEvidence,
858
+ };
859
+ }
860
+ function resolveRoleApiStyleHint(detection, stackId, component) {
861
+ if (component?.apiStyle) {
862
+ return component.apiStyle;
863
+ }
864
+ if (!stackId) {
865
+ return undefined;
866
+ }
867
+ const candidate = detection.stacks.candidates?.find((entry) => entry.adapterId === stackId);
868
+ return candidate?.apiStyleHint ?? detection.apiDbHints?.api.apiStyle;
869
+ }
870
+ function resolveStackSelections(detection, projectKinds) {
871
+ const selections = {};
872
+ const backendComponents = getBackendComponents(detection);
873
+ const frontendComponents = getFrontendComponents(detection);
874
+ const libraryComponents = [...getLibraryComponents(detection), ...getDataComponents(detection)];
875
+ if (projectKinds.includes('backend')) {
876
+ const backendComponentStacks = uniqueInOrder(backendComponents
877
+ .map((component) => component.stack)
878
+ .filter((stack) => Boolean(stack)));
879
+ const detectedBackendStacks = uniqueInOrder(detection.stacks.backendStacks);
880
+ const preferredBackend = detection.topology.topology === 'monorepo'
881
+ ? (resolveDominantBackendFrameworkFromComponents(detection.detectedComponents ?? []) ??
882
+ resolveConcreteBackendStackSelection(backendComponentStacks.length > 0 ? backendComponentStacks : detectedBackendStacks)).selected
883
+ : (resolveConcreteBackendStackSelection(detectedBackendStacks).selected ??
884
+ resolveSingleProjectBackendStackFromRuntimeComponent(detection, backendComponents, detectedBackendStacks));
885
+ if (preferredBackend) {
886
+ selections.backend = preferredBackend;
887
+ }
888
+ }
889
+ if (projectKinds.includes('frontend')) {
890
+ const frontendComponentStack = frontendComponents
891
+ .map((component) => component.stack)
892
+ .find((stack) => Boolean(stack));
893
+ const preferredFrontend = detection.topology.topology === 'monorepo'
894
+ ? frontendComponentStack
895
+ : detection.stacks.frontendStacks[0];
896
+ if (preferredFrontend) {
897
+ selections.frontend = preferredFrontend;
898
+ }
899
+ }
900
+ if (projectKinds.includes('library')) {
901
+ const libraryComponentStacks = uniqueInOrder(libraryComponents
902
+ .map((component) => component.stack)
903
+ .filter((stack) => isGenericLibraryStack(stack)));
904
+ const selectedLibrary = detection.topology.topology === 'monorepo'
905
+ ? libraryComponentStacks.length === 1
906
+ ? libraryComponentStacks[0]
907
+ : undefined
908
+ : detection.stacks.selectedAdapterId;
909
+ if (isGenericLibraryStack(selectedLibrary)) {
910
+ selections.library = selectedLibrary;
911
+ }
912
+ }
913
+ return selections;
914
+ }
915
+ function resolveProjectProfiles(detection, projectKinds, stackSelections, apiStyle) {
916
+ const profiles = {};
917
+ for (const kind of projectKinds) {
918
+ if (kind === 'library') {
919
+ continue;
920
+ }
921
+ const stackId = stackSelections[kind];
922
+ if (!stackId) {
923
+ continue;
924
+ }
925
+ const component = detection.detectedComponents.find((entry) => entry.stack === stackId && entry.kind === kind);
926
+ profiles[kind] = (0, implementationProfileResolution_1.resolveImplementationProfile)({
927
+ selectedAdapterId: stackId,
928
+ selectedAdapterCategory: (0, stackAdapters_1.getStackAdapterById)(stackId).category,
929
+ projectKinds: [kind],
930
+ apiStyleHint: kind === 'backend' && apiStyle
931
+ ? apiStyle
932
+ : resolveRoleApiStyleHint(detection, stackId, component),
933
+ databaseHints: detection.toolingHints?.databaseHints,
934
+ toolingHints: detection.toolingHints?.toolingHints,
935
+ repoTopology: detection.topology.topology === 'unknown' ? 'single_project' : detection.topology.topology,
936
+ });
937
+ }
938
+ return profiles;
939
+ }
940
+ function resolveProposalImplementationProfile(detection, projectKinds, stackSelections, evidence, apiStyle) {
941
+ if (projectKinds.includes('backend') && !stackSelections.backend) {
942
+ return undefined;
943
+ }
944
+ const selectedAdapterId = detection.topology.topology === 'monorepo' && stackSelections.backend
945
+ ? stackSelections.backend
946
+ : detection.stacks.selectedAdapterId;
947
+ const backendComponent = stackSelections.backend
948
+ ? getBackendComponents(detection).find((component) => component.stack === stackSelections.backend)
949
+ : undefined;
950
+ const selectedAdapter = (0, stackAdapters_1.getStackAdapterById)(selectedAdapterId);
951
+ const selectedCandidate = getSelectedCandidate(detection);
952
+ const profile = (0, implementationProfileResolution_1.resolveImplementationProfile)({
953
+ selectedAdapterId,
954
+ selectedAdapterCategory: detection.stacks.selectedAdapterCategory ?? selectedAdapter.category,
955
+ projectKinds: [...projectKinds],
956
+ apiStyleHint: apiStyle ?? backendComponent?.apiStyle ?? selectedCandidate?.apiStyleHint ?? detection.apiDbHints?.api.apiStyle,
957
+ databaseHints: detection.toolingHints?.databaseHints,
958
+ toolingHints: detection.toolingHints?.toolingHints,
959
+ repoTopology: detection.topology.topology === 'unknown' ? 'single_project' : detection.topology.topology,
960
+ });
961
+ if (selectedAdapterId) {
962
+ addEvidence(evidence, `Resolved implementation profile from selected adapter '${selectedAdapterId}'`);
963
+ }
964
+ return profile === 'other' ? undefined : profile;
965
+ }
966
+ function resolveProposalModulesRoot(detection) {
967
+ const modulesRoot = detection.modules?.modulesRoot;
968
+ if (!modulesRoot || detection.topology.topology !== 'monorepo') {
969
+ return modulesRoot;
970
+ }
971
+ if (['apps', 'packages', 'services', 'libs'].includes(modulesRoot)) {
972
+ const componentContainers = new Set((detection.detectedComponents ?? [])
973
+ .map((component) => component.path.split('/')[0])
974
+ .filter((segment) => ['apps', 'packages', 'services', 'libs'].includes(segment ?? '')));
975
+ return componentContainers.size <= 1 ? modulesRoot : undefined;
976
+ }
977
+ const componentPaths = (detection.detectedComponents ?? [])
978
+ .map((component) => component.path)
979
+ .filter((componentPath) => componentPath !== '.' && componentPath.length > 0);
980
+ if (componentPaths.some((componentPath) => modulesRoot.startsWith(`${componentPath}/`))) {
981
+ return undefined;
982
+ }
983
+ return modulesRoot;
984
+ }
985
+ function resolveProposalApiStyle(detection) {
986
+ if (detection.topology.topology === 'monorepo') {
987
+ const apiStyle = getBackendComponents(detection)
988
+ .map((component) => component.apiStyle)
989
+ .find((value) => value === 'rest' || value === 'graphql');
990
+ return apiStyle ?? 'none';
991
+ }
992
+ const detectedApiStyle = detection.apiDbHints?.api.apiStyle;
993
+ return detectedApiStyle && detectedApiStyle !== 'unknown' ? detectedApiStyle : undefined;
994
+ }
995
+ function resolveProposalDatabase(input) {
996
+ const toSupportedProposalDatabase = (value) => {
997
+ if (value === 'postgresql' || value === 'mysql' || value === 'none') {
998
+ return value;
999
+ }
1000
+ return undefined;
1001
+ };
1002
+ const supportedDatabaseHints = uniqueSorted(input.databaseHints
1003
+ .map((value) => toSupportedProposalDatabase(value))
1004
+ .filter((value) => Boolean(value)));
1005
+ const singleSupportedHint = supportedDatabaseHints.length === 1 ? supportedDatabaseHints[0] : undefined;
1006
+ const detectedDatabase = input.detection.apiDbHints?.database.database;
1007
+ if (input.detection.topology.topology === 'monorepo') {
1008
+ const componentDatabase = getBackendComponents(input.detection)
1009
+ .map((component) => component.database)
1010
+ .find((value) => Boolean(value) && value !== 'unknown' && value !== 'none');
1011
+ const supportedComponentDatabase = toSupportedProposalDatabase(componentDatabase);
1012
+ if (supportedComponentDatabase) {
1013
+ return supportedComponentDatabase;
1014
+ }
1015
+ if (hasAttributableBackendRoot(input.detection)) {
1016
+ return toSupportedProposalDatabase(detectedDatabase) ??
1017
+ singleSupportedHint ??
1018
+ 'none';
1019
+ }
1020
+ return 'none';
1021
+ }
1022
+ return toSupportedProposalDatabase(detectedDatabase) ??
1023
+ singleSupportedHint ??
1024
+ 'none';
1025
+ }
1026
+ function resolveConfidence(proposal, detection) {
1027
+ let score = 0;
1028
+ if (proposal.projectKinds && proposal.projectKinds.length > 0) {
1029
+ score += 1;
1030
+ }
1031
+ if (proposal.architectureStyle) {
1032
+ score += 2;
1033
+ }
1034
+ if (proposal.modulesRoot) {
1035
+ score += 1;
1036
+ }
1037
+ if (proposal.apiStyle) {
1038
+ score += 1;
1039
+ }
1040
+ if (proposal.database) {
1041
+ score += 1;
1042
+ }
1043
+ if (proposal.implementationProfile) {
1044
+ score += 1;
1045
+ }
1046
+ if ((proposal.modules?.length ?? 0) > 0) {
1047
+ score += 1;
1048
+ }
1049
+ if ((proposal.toolingHints?.length ?? 0) > 0) {
1050
+ score += 1;
1051
+ }
1052
+ if (detection.topology.confidence === 'high') {
1053
+ score += 1;
1054
+ }
1055
+ if (detection.stacks.confidence === 'high') {
1056
+ score += 1;
1057
+ }
1058
+ if (detection.modules?.confidence === 'high') {
1059
+ score += 1;
1060
+ }
1061
+ if (detection.apiDbHints?.confidence === 'high') {
1062
+ score += 1;
1063
+ }
1064
+ if (detection.toolingHints?.confidence === 'high') {
1065
+ score += 1;
1066
+ }
1067
+ if (score >= 9) {
1068
+ return 'high';
1069
+ }
1070
+ if (score >= 5) {
1071
+ return 'medium';
1072
+ }
1073
+ return 'low';
1074
+ }
1075
+ function resolveProposalReasons(input) {
1076
+ const reasons = {};
1077
+ const selectedStackIds = uniqueSorted(Object.values(input.stackSelections)
1078
+ .filter((stack) => Boolean(stack)));
1079
+ const selectedStackReasons = selectedStackIds
1080
+ .flatMap((stackId) => input.detection.stacks.candidates?.find((candidate) => candidate.adapterId === stackId)?.evidence ?? []);
1081
+ if (selectedStackReasons.length > 0) {
1082
+ reasons.framework = uniqueLimited(selectedStackReasons);
1083
+ }
1084
+ if (input.apiStyle && input.apiStyle !== 'unknown') {
1085
+ reasons.api = input.apiStyle === 'none'
1086
+ ? uniqueLimited(input.detection.apiDbHints?.api.evidence ?? ['No API artifacts detected for current project profile'], 4)
1087
+ : uniqueLimited(input.detection.apiDbHints?.api.evidence ?? [], 4);
1088
+ }
1089
+ if (input.database && input.database !== 'unknown') {
1090
+ const databaseEvidence = [
1091
+ ...(input.detection.apiDbHints?.database.evidence ?? []),
1092
+ ...(input.detection.toolingHints?.evidence ?? []).filter((entry) => /prisma|datasource|database|orm|jpa|entity framework|sqlalchemy|alembic/iu.test(entry)),
1093
+ ];
1094
+ reasons.database = input.database === 'none'
1095
+ ? uniqueLimited(databaseEvidence.length > 0 ? databaseEvidence : ['No database artifacts detected for current project profile'], 4)
1096
+ : uniqueLimited(databaseEvidence, 4);
1097
+ }
1098
+ if ((input.detection.toolingHints?.toolingHints.length ?? 0) > 0) {
1099
+ reasons.tooling = uniqueLimited(input.detection.toolingHints?.evidence ?? [], 5);
1100
+ }
1101
+ if (input.architectureStyleEvidence.length > 0) {
1102
+ reasons.architectureStyle = uniqueLimited(input.architectureStyleEvidence, 5);
1103
+ }
1104
+ if (input.modulesRoot || (input.moduleRoots?.length ?? 0) > 0) {
1105
+ reasons.modulesRoot = uniqueLimited([
1106
+ ...(input.modulesRoot ? [`Detected modules root at ${input.modulesRoot}`] : []),
1107
+ ...((input.moduleRoots ?? []).length > 0 ? [`Detected module roots: ${(input.moduleRoots ?? []).join(', ')}`] : []),
1108
+ ...(input.detection.modules?.evidence ?? []),
1109
+ ], 4);
1110
+ }
1111
+ return Object.keys(reasons).length > 0 ? reasons : undefined;
1112
+ }
1113
+ function buildArchitectureProposal(detection) {
1114
+ const evidence = [];
1115
+ addEvidence(evidence, `Detected ${detection.topology.topology} topology`);
1116
+ const projectKinds = resolveNormalizedProjectKinds(detection, evidence);
1117
+ const architectureStyleDecision = resolveArchitectureStyle(detection, projectKinds, evidence);
1118
+ const architectureStyle = architectureStyleDecision.architectureStyle;
1119
+ const stackSelections = resolveStackSelections(detection, projectKinds);
1120
+ const apiStyle = resolveProposalApiStyle(detection);
1121
+ const projectProfiles = resolveProjectProfiles(detection, projectKinds, stackSelections, apiStyle);
1122
+ const implementationProfile = resolveProposalImplementationProfile(detection, projectKinds, stackSelections, evidence, apiStyle);
1123
+ const modulesRoot = resolveProposalModulesRoot(detection);
1124
+ const moduleRoots = detection.modules?.moduleRoots;
1125
+ if (modulesRoot) {
1126
+ addEvidence(evidence, `Detected modules root at ${modulesRoot}`);
1127
+ }
1128
+ else if ((moduleRoots?.length ?? 0) > 0) {
1129
+ addEvidence(evidence, `Detected module roots: ${moduleRoots.join(', ')}`);
1130
+ }
1131
+ if (apiStyle) {
1132
+ addEvidence(evidence, apiStyle === 'none' ? 'Detected no public API signals' : `Detected ${apiStyle.toUpperCase()} API`);
1133
+ }
1134
+ const detectedDatabase = detection.apiDbHints?.database.database;
1135
+ const databaseHints = uniqueSorted([
1136
+ ...(detection.toolingHints?.databaseHints ?? []),
1137
+ ...(detection.apiDbHints?.database.databaseHints ?? []),
1138
+ ...(detectedDatabase && detectedDatabase !== 'unknown' && detectedDatabase !== 'none'
1139
+ ? [detectedDatabase]
1140
+ : []),
1141
+ ]);
1142
+ const database = resolveProposalDatabase({ detection, databaseHints });
1143
+ const authStyle = apiStyle && apiStyle !== 'none' ? 'jwt' : 'none';
1144
+ const components = resolveProposalComponents(detection);
1145
+ const resources = resolveProposalResources({ detection, database, apiStyle });
1146
+ const moduleCandidates = resolveModuleCandidates(detection, resources ?? []);
1147
+ const moduleCandidateGroups = groupModuleCandidates(moduleCandidates);
1148
+ const modules = moduleCandidates
1149
+ .filter((module) => module.selectedByDefault)
1150
+ .map((module) => module.name)
1151
+ .filter((name) => name.length > 0);
1152
+ if (moduleCandidates.length > 0) {
1153
+ addEvidence(evidence, `Detected ${moduleCandidates.length} module candidates`);
1154
+ }
1155
+ if (modules.length > 0) {
1156
+ addEvidence(evidence, `Preselected ${modules.length} architecture module candidates`);
1157
+ }
1158
+ if (database && database !== 'none') {
1159
+ addEvidence(evidence, `Detected ${database} database`);
1160
+ }
1161
+ else if (database === 'none') {
1162
+ addEvidence(evidence, 'Detected no database signals');
1163
+ }
1164
+ if (authStyle === 'none') {
1165
+ addEvidence(evidence, 'Detected no authentication boundary signals');
1166
+ }
1167
+ if ((detection.toolingHints?.toolingHints.length ?? 0) > 0) {
1168
+ addEvidence(evidence, `Detected tooling hints: ${(detection.toolingHints?.toolingHints ?? []).join(', ')}`);
1169
+ }
1170
+ const proposalReasons = resolveProposalReasons({
1171
+ detection,
1172
+ stackSelections,
1173
+ architectureStyleEvidence: architectureStyleDecision.architectureStyleEvidence,
1174
+ modulesRoot,
1175
+ moduleRoots,
1176
+ apiStyle,
1177
+ database,
1178
+ });
1179
+ const proposalCore = {
1180
+ ...(projectKinds.length > 0 ? { projectKinds } : {}),
1181
+ ...(architectureStyle ? { architectureStyle } : {}),
1182
+ ...((architectureStyleDecision.architectureStyleEvidence.length ?? 0) > 0
1183
+ ? { architectureStyleEvidence: uniqueSorted(architectureStyleDecision.architectureStyleEvidence) }
1184
+ : {}),
1185
+ ...(modulesRoot ? { modulesRoot } : {}),
1186
+ ...((moduleRoots?.length ?? 0) > 0 ? { moduleRoots: uniqueSorted(moduleRoots ?? []) } : {}),
1187
+ ...(apiStyle ? { apiStyle } : {}),
1188
+ ...(database ? { database } : {}),
1189
+ ...(authStyle ? { authStyle } : {}),
1190
+ ...(databaseHints.length > 0 ? { databaseHints } : {}),
1191
+ ...(implementationProfile ? { implementationProfile } : {}),
1192
+ ...(Object.keys(stackSelections).length > 0 ? { stackSelections } : {}),
1193
+ ...(Object.keys(projectProfiles).length > 0 ? { projectProfiles } : {}),
1194
+ ...(components ? { components } : {}),
1195
+ ...(resources ? { resources } : {}),
1196
+ ...((detection.toolingHints?.toolingHints.length ?? 0) > 0
1197
+ ? { toolingHints: uniqueSorted(detection.toolingHints?.toolingHints ?? []) }
1198
+ : {}),
1199
+ ...(modules.length > 0 ? { modules: uniqueSorted(modules) } : {}),
1200
+ ...(moduleCandidates.length > 0 ? { moduleCandidates } : {}),
1201
+ ...(moduleCandidateGroups ? { moduleCandidateGroups } : {}),
1202
+ ...((detection.detectedComponents?.length ?? 0) > 0 ? { detectedComponents: detection.detectedComponents } : {}),
1203
+ ...(proposalReasons ? { proposalReasons } : {}),
1204
+ };
1205
+ const confidence = resolveConfidence(proposalCore, detection);
1206
+ if (architectureStyle) {
1207
+ addEvidence(evidence, `Proposed ${architectureStyle} architecture`);
1208
+ }
1209
+ return {
1210
+ ...proposalCore,
1211
+ confidence,
1212
+ evidence: uniqueSorted(evidence),
1213
+ };
1214
+ }
1215
+ //# sourceMappingURL=architectureProposalBuilder.js.map