@archpilotlabs/archpilot 0.0.8 → 0.0.10

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.
@@ -210932,6 +210932,7 @@ var import_node_fs = require("node:fs");
210932
210932
  var validProjectKinds = /* @__PURE__ */ new Set(["backend", "frontend", "library"]);
210933
210933
  var validCanonicalStackIds = /* @__PURE__ */ new Set([
210934
210934
  "node_typescript",
210935
+ "express",
210935
210936
  "nestjs",
210936
210937
  "java",
210937
210938
  "spring",
@@ -210940,9 +210941,11 @@ var validCanonicalStackIds = /* @__PURE__ */ new Set([
210940
210941
  "flask",
210941
210942
  "python",
210942
210943
  "php",
210944
+ "laravel",
210943
210945
  "go",
210944
210946
  "dotnet",
210945
210947
  "ruby_rails",
210948
+ "kotlin",
210946
210949
  "kotlin_spring",
210947
210950
  "rust",
210948
210951
  "react",
@@ -210956,6 +210959,7 @@ var validCanonicalStackIds = /* @__PURE__ */ new Set([
210956
210959
  ]);
210957
210960
  var canonicalStackLabels = {
210958
210961
  node_typescript: "TypeScript / Node.js",
210962
+ express: "Express.js",
210959
210963
  nestjs: "NestJS",
210960
210964
  java: "Java",
210961
210965
  spring: "Spring",
@@ -210964,9 +210968,11 @@ var canonicalStackLabels = {
210964
210968
  flask: "Flask",
210965
210969
  python: "Python",
210966
210970
  php: "PHP",
210971
+ laravel: "Laravel",
210967
210972
  go: "Go",
210968
210973
  dotnet: ".NET",
210969
210974
  ruby_rails: "Ruby / Rails",
210975
+ kotlin: "Kotlin",
210970
210976
  kotlin_spring: "Kotlin / Spring",
210971
210977
  rust: "Rust",
210972
210978
  react: "React",
@@ -211177,6 +211183,103 @@ var nodeTypescriptAdapter = buildAdapter({
211177
211183
  var stackAdaptersById = {
211178
211184
  other: genericDefaultAdapter,
211179
211185
  node_typescript: nodeTypescriptAdapter,
211186
+ express: buildAdapter({
211187
+ id: "express",
211188
+ displayName: canonicalStackLabels.express,
211189
+ ecosystem: "node",
211190
+ category: "framework",
211191
+ priority: 70,
211192
+ manifestDetectionHints: ["package.json", "tsconfig.json", "app.js", "app.ts", "server.js", "server.ts"],
211193
+ buildToolHints: ["npm", "pnpm", "yarn"],
211194
+ defaults: {
211195
+ sourceRoots: ["src", "."],
211196
+ moduleRootCandidates: ["src/modules", "src/features", "routes", "src/routes", "src"],
211197
+ apiRoots: ["routes", "src/routes", "src/api", "api"],
211198
+ databaseRoots: ["prisma", "db", "database", "migrations"],
211199
+ configFiles: ["package.json", "tsconfig.json"],
211200
+ fileExtensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"],
211201
+ dependencyFileExtensions: [".ts", ".js", ".mjs", ".cjs"],
211202
+ ignoreDirectories: ["node_modules", "dist", "coverage"],
211203
+ entrypointHints: ["app.ts", "server.ts", "src/app.ts", "src/server.ts", "app.js", "server.js", "src/app.js", "src/server.js"],
211204
+ publicEntrypointDirectories: ["public"],
211205
+ moduleIndexFileName: "index.ts"
211206
+ },
211207
+ detect: (context) => {
211208
+ const evidence = [];
211209
+ let strongSignals = 0;
211210
+ let mediumSignals = 0;
211211
+ let score = 0;
211212
+ const hasExpressDependency = context.hasPackageDependency("express");
211213
+ const hasExpressFactory = context.hasAnyFileMatchingPattern(
211214
+ /\.(ts|js|mjs|cjs)$/iu,
211215
+ /\bexpress\s*\(\s*\)/u
211216
+ );
211217
+ const hasExpressImport = context.hasAnyFileMatchingPattern(
211218
+ /\.(ts|js|mjs|cjs)$/iu,
211219
+ /\b(import\s+express\s+from\s+['"]express['"]|require\s*\(\s*['"]express['"]\s*\))/u
211220
+ );
211221
+ const hasAppUse = context.hasAnyFileMatchingPattern(
211222
+ /\.(ts|js|mjs|cjs)$/iu,
211223
+ /\bapp\.use\s*\(/u
211224
+ );
211225
+ const hasRouterUsage = context.hasAnyFileMatchingPattern(
211226
+ /\.(ts|js|mjs|cjs)$/iu,
211227
+ /\b(router|Router)\s*\.\s*(get|post|put|patch|delete|use)\s*\(/u
211228
+ );
211229
+ const hasRoutesDirectory = context.hasDirectory("routes") || context.hasDirectory("src/routes");
211230
+ const hasRouteFiles = context.hasAnyPathMatchingPattern(/(^|\/)(routes|src\/routes)\/.+\.(ts|js|mjs|cjs)$/iu);
211231
+ if (hasExpressDependency) {
211232
+ strongSignals += 1;
211233
+ score += 46;
211234
+ evidence.push("Found package.json with express dependency");
211235
+ }
211236
+ if (hasExpressFactory) {
211237
+ strongSignals += 1;
211238
+ score += 28;
211239
+ evidence.push("Found express() app construction");
211240
+ }
211241
+ if (hasExpressImport) {
211242
+ mediumSignals += 1;
211243
+ score += 12;
211244
+ evidence.push("Found Express import or require");
211245
+ }
211246
+ if (hasAppUse) {
211247
+ mediumSignals += 1;
211248
+ score += 10;
211249
+ evidence.push("Found app.use(...) middleware or router mounting");
211250
+ }
211251
+ if (hasRouterUsage) {
211252
+ mediumSignals += 1;
211253
+ score += 10;
211254
+ evidence.push("Found router HTTP method usage");
211255
+ }
211256
+ if (hasRoutesDirectory) {
211257
+ mediumSignals += 1;
211258
+ score += 6;
211259
+ evidence.push("Found routes/ or src/routes/ directory");
211260
+ }
211261
+ if (hasRouteFiles) {
211262
+ mediumSignals += 1;
211263
+ score += 5;
211264
+ evidence.push("Found route files under routes/");
211265
+ }
211266
+ const hasSafeExpressEvidence = hasExpressDependency && (hasExpressFactory || hasExpressImport || hasAppUse || hasRouterUsage || hasRoutesDirectory);
211267
+ if (!hasSafeExpressEvidence) {
211268
+ return null;
211269
+ }
211270
+ const confidence = strongSignals >= 2 || strongSignals >= 1 && mediumSignals >= 2 ? "high" : strongSignals >= 1 ? "medium" : "low";
211271
+ return toResult({
211272
+ adapterId: "express",
211273
+ confidence,
211274
+ score,
211275
+ evidence,
211276
+ projectKindHints: ["backend"],
211277
+ apiStyleHint: "rest"
211278
+ });
211279
+ },
211280
+ resolveProjectKinds: () => ["backend"],
211281
+ getImportExtractionMode: () => "typescript-imports"
211282
+ }),
211180
211283
  nestjs: buildAdapter({
211181
211284
  id: "nestjs",
211182
211285
  displayName: canonicalStackLabels.nestjs,
@@ -211452,9 +211555,9 @@ var stackAdaptersById = {
211452
211555
  ],
211453
211556
  buildToolHints: ["maven", "gradle"],
211454
211557
  defaults: {
211455
- sourceRoots: ["src/main/java", "src"],
211456
- moduleRootCandidates: ["src/main/java"],
211457
- apiRoots: ["src/main/java"],
211558
+ sourceRoots: ["src/main/java", "src/main/kotlin", "src"],
211559
+ moduleRootCandidates: ["src/main/java", "src/main/kotlin"],
211560
+ apiRoots: ["src/main/java", "src/main/kotlin"],
211458
211561
  databaseRoots: ["src/main/resources", "src/main/resources/db"],
211459
211562
  configFiles: [
211460
211563
  "pom.xml",
@@ -211474,7 +211577,7 @@ var stackAdaptersById = {
211474
211577
  "out",
211475
211578
  ...commonIgnoreDirectories
211476
211579
  ],
211477
- entrypointHints: ["src/main/java"],
211580
+ entrypointHints: ["src/main/java", "src/main/kotlin", "Application.java", "Application.kt"],
211478
211581
  publicEntrypointDirectories: [],
211479
211582
  moduleIndexFileName: "Application.java"
211480
211583
  },
@@ -211500,7 +211603,7 @@ var stackAdaptersById = {
211500
211603
  /\.(java|kt)$/iu,
211501
211604
  /@SpringBootApplication\b/u
211502
211605
  );
211503
- const hasMainJavaWithDependencies = context.hasDirectory("src/main/java") && hasSpringDependencyEvidence;
211606
+ const hasMainSourceWithDependencies = (context.hasDirectory("src/main/java") || context.hasDirectory("src/main/kotlin")) && hasSpringDependencyEvidence;
211504
211607
  const hasApplicationConfigWithDependencies = (context.hasFile("application.yml") || context.hasFile("application.yaml") || context.hasFile("application.properties")) && hasSpringDependencyEvidence;
211505
211608
  const hasRestController = context.hasAnyFileMatchingPattern(
211506
211609
  /\.(java|kt)$/iu,
@@ -211524,9 +211627,9 @@ var stackAdaptersById = {
211524
211627
  );
211525
211628
  const hasResourcesDirectory = context.hasDirectory("src/main/resources");
211526
211629
  const hasWrapperWithSpringEvidence = (context.hasFile("mvnw") || context.hasFile("gradlew")) && hasSpringDependencyEvidence;
211527
- const hasTestJava = context.hasDirectory("src/test/java");
211630
+ const hasTestJvmSource = context.hasDirectory("src/test/java") || context.hasDirectory("src/test/kotlin");
211528
211631
  const hasBootstrapConfig = context.hasFile("bootstrap.yml") || context.hasFile("bootstrap.yaml");
211529
- const hasJavaPackageShape = context.hasDirectory("src/main/java/com") || context.hasDirectory("src/main/java/org");
211632
+ const hasJvmPackageShape = context.hasDirectory("src/main/java/com") || context.hasDirectory("src/main/java/org") || context.hasDirectory("src/main/kotlin/com") || context.hasDirectory("src/main/kotlin/org");
211530
211633
  if (hasPomSpringDependency) {
211531
211634
  strongSignals += 1;
211532
211635
  score += 54;
@@ -211542,10 +211645,10 @@ var stackAdaptersById = {
211542
211645
  score += 44;
211543
211646
  evidence.push("Found @SpringBootApplication in source");
211544
211647
  }
211545
- if (hasMainJavaWithDependencies) {
211648
+ if (hasMainSourceWithDependencies) {
211546
211649
  strongSignals += 1;
211547
211650
  score += 20;
211548
- evidence.push("Found src/main/java with Spring dependency evidence");
211651
+ evidence.push("Found JVM main source root with Spring dependency evidence");
211549
211652
  }
211550
211653
  if (hasApplicationConfigWithDependencies) {
211551
211654
  strongSignals += 1;
@@ -211587,22 +211690,22 @@ var stackAdaptersById = {
211587
211690
  score += 4;
211588
211691
  evidence.push("Found Maven/Gradle wrapper with Spring dependency evidence");
211589
211692
  }
211590
- if (hasTestJava) {
211693
+ if (hasTestJvmSource) {
211591
211694
  weakSignals += 1;
211592
211695
  score += 2;
211593
- evidence.push("Found src/test/java directory");
211696
+ evidence.push("Found JVM test source directory");
211594
211697
  }
211595
211698
  if (hasBootstrapConfig) {
211596
211699
  weakSignals += 1;
211597
211700
  score += 2;
211598
211701
  evidence.push("Found bootstrap.yml or bootstrap.yaml");
211599
211702
  }
211600
- if (hasJavaPackageShape) {
211703
+ if (hasJvmPackageShape) {
211601
211704
  weakSignals += 1;
211602
211705
  score += 1;
211603
- evidence.push("Found conventional Java package directory under src/main/java");
211706
+ evidence.push("Found conventional JVM package directory under main source root");
211604
211707
  }
211605
- const hasSafeSpringCoreEvidence = hasSpringDependencyEvidence || hasSpringBootApplication || (hasRestController || hasController || hasService || hasRepository || hasMappingAnnotation) && (context.hasDirectory("src/main/java") || hasResourcesDirectory);
211708
+ const hasSafeSpringCoreEvidence = hasSpringDependencyEvidence || hasSpringBootApplication || (hasRestController || hasController || hasService || hasRepository || hasMappingAnnotation) && (context.hasDirectory("src/main/java") || context.hasDirectory("src/main/kotlin") || hasResourcesDirectory);
211606
211709
  if (!hasSafeSpringCoreEvidence || strongSignals === 0) {
211607
211710
  return null;
211608
211711
  }
@@ -212191,6 +212294,101 @@ var stackAdaptersById = {
212191
212294
  resolveProjectKinds: () => ["backend", "library"],
212192
212295
  getImportExtractionMode: () => "python-imports"
212193
212296
  }),
212297
+ laravel: buildAdapter({
212298
+ id: "laravel",
212299
+ displayName: canonicalStackLabels.laravel,
212300
+ ecosystem: "php",
212301
+ category: "framework",
212302
+ priority: 70,
212303
+ manifestDetectionHints: ["composer.json", "artisan", "web.php", "api.php", "app.php"],
212304
+ buildToolHints: ["composer", "artisan"],
212305
+ defaults: {
212306
+ sourceRoots: ["app", "routes", "config"],
212307
+ moduleRootCandidates: ["app/Modules", "app/Services", "app/Http/Controllers", "app"],
212308
+ apiRoots: ["routes", "app/Http/Controllers"],
212309
+ databaseRoots: ["database", "database/migrations"],
212310
+ configFiles: ["composer.json", "artisan", "config/app.php"],
212311
+ fileExtensions: [".php"],
212312
+ dependencyFileExtensions: [".php"],
212313
+ ignoreDirectories: [
212314
+ "vendor",
212315
+ "storage",
212316
+ "bootstrap/cache",
212317
+ "public",
212318
+ ...commonIgnoreDirectories
212319
+ ],
212320
+ entrypointHints: ["artisan", "public/index.php", "bootstrap/app.php"],
212321
+ publicEntrypointDirectories: [],
212322
+ moduleIndexFileName: "index.php"
212323
+ },
212324
+ detect: (context) => {
212325
+ const evidence = [];
212326
+ let strongSignals = 0;
212327
+ let mediumSignals = 0;
212328
+ let score = 0;
212329
+ const hasLaravelComposer = context.hasFileMatchingPattern(
212330
+ "composer.json",
212331
+ /["']laravel\/framework["']/iu
212332
+ );
212333
+ const hasArtisan = context.hasFile("artisan");
212334
+ const hasRoutesWeb = context.hasAnyPathMatchingPattern(/(^|\/)routes\/web\.php$/iu);
212335
+ const hasRoutesApi = context.hasAnyPathMatchingPattern(/(^|\/)routes\/api\.php$/iu);
212336
+ const hasControllers = context.hasDirectory("app/Http/Controllers");
212337
+ const hasConfigApp = context.hasAnyPathMatchingPattern(/(^|\/)config\/app\.php$/iu);
212338
+ const hasLaravelRouteFacade = context.hasAnyFileMatchingPattern(
212339
+ /(^|\/)routes\/.+\.php$/iu,
212340
+ /\bRoute::(get|post|put|patch|delete|middleware|group)\s*\(/u
212341
+ );
212342
+ if (hasLaravelComposer) {
212343
+ strongSignals += 1;
212344
+ score += 50;
212345
+ evidence.push("Found composer.json with laravel/framework dependency");
212346
+ }
212347
+ if (hasArtisan) {
212348
+ strongSignals += 1;
212349
+ score += 18;
212350
+ evidence.push("Found artisan entrypoint");
212351
+ }
212352
+ if (hasRoutesWeb) {
212353
+ mediumSignals += 1;
212354
+ score += 10;
212355
+ evidence.push("Found routes/web.php");
212356
+ }
212357
+ if (hasRoutesApi) {
212358
+ mediumSignals += 1;
212359
+ score += 10;
212360
+ evidence.push("Found routes/api.php");
212361
+ }
212362
+ if (hasControllers) {
212363
+ mediumSignals += 1;
212364
+ score += 10;
212365
+ evidence.push("Found app/Http/Controllers directory");
212366
+ }
212367
+ if (hasConfigApp) {
212368
+ mediumSignals += 1;
212369
+ score += 6;
212370
+ evidence.push("Found config/app.php");
212371
+ }
212372
+ if (hasLaravelRouteFacade) {
212373
+ mediumSignals += 1;
212374
+ score += 8;
212375
+ evidence.push("Found Laravel Route facade usage");
212376
+ }
212377
+ if (!hasLaravelComposer || !(hasArtisan || hasRoutesWeb || hasRoutesApi || hasControllers)) {
212378
+ return null;
212379
+ }
212380
+ return toResult({
212381
+ adapterId: "laravel",
212382
+ confidence: strongSignals >= 2 || mediumSignals >= 2 ? "high" : "medium",
212383
+ score,
212384
+ evidence,
212385
+ projectKindHints: ["backend"],
212386
+ apiStyleHint: hasRoutesApi || hasLaravelRouteFacade ? "rest" : "unknown"
212387
+ });
212388
+ },
212389
+ resolveProjectKinds: () => ["backend"],
212390
+ getImportExtractionMode: () => "php-imports"
212391
+ }),
212194
212392
  php: buildAdapter({
212195
212393
  id: "php",
212196
212394
  displayName: canonicalStackLabels.php,
@@ -212478,23 +212676,182 @@ var stackAdaptersById = {
212478
212676
  displayName: canonicalStackLabels.ruby_rails,
212479
212677
  ecosystem: "other",
212480
212678
  category: "framework",
212481
- priority: 20,
212482
- manifestDetectionHints: ["Gemfile"],
212679
+ priority: 70,
212680
+ manifestDetectionHints: ["Gemfile", "rails", "routes.rb", "application.rb"],
212483
212681
  buildToolHints: ["bundle"],
212484
212682
  defaults: {
212485
- sourceRoots: ["app"],
212486
- moduleRootCandidates: ["app"],
212487
- apiRoots: ["app/controllers"],
212683
+ sourceRoots: ["app", "config"],
212684
+ moduleRootCandidates: ["app/services", "app/controllers", "app/models", "app"],
212685
+ apiRoots: ["app/controllers", "config/routes.rb"],
212488
212686
  databaseRoots: ["db"],
212489
- configFiles: ["Gemfile"],
212687
+ configFiles: ["Gemfile", "config/routes.rb", "config/application.rb"],
212490
212688
  fileExtensions: [".rb"],
212491
212689
  dependencyFileExtensions: [".rb"],
212492
- ignoreDirectories: [...commonIgnoreDirectories],
212690
+ ignoreDirectories: ["vendor", "tmp", "log", ...commonIgnoreDirectories],
212493
212691
  entrypointHints: ["config/application.rb"],
212494
212692
  publicEntrypointDirectories: [],
212495
212693
  moduleIndexFileName: "application.rb"
212496
212694
  },
212497
- detect: () => null,
212695
+ detect: (context) => {
212696
+ const evidence = [];
212697
+ let strongSignals = 0;
212698
+ let mediumSignals = 0;
212699
+ let score = 0;
212700
+ const hasRailsGem = context.hasFileMatchingPattern("Gemfile", /\bgem\s+['"]rails['"]/u);
212701
+ const hasBinRails = context.hasAnyPathMatchingPattern(/(^|\/)bin\/rails$/u);
212702
+ const hasRoutes = context.hasAnyPathMatchingPattern(/(^|\/)config\/routes\.rb$/u);
212703
+ const hasControllers = context.hasDirectory("app/controllers");
212704
+ const hasModels = context.hasDirectory("app/models");
212705
+ const hasServices = context.hasDirectory("app/services");
212706
+ const hasRailsRoutesDraw = context.hasFileMatchingPattern("routes.rb", /\bRails\.application\.routes\.draw\b/u);
212707
+ const hasApplicationClass = context.hasAnyFileMatchingPattern(
212708
+ /(^|\/)config\/application\.rb$/u,
212709
+ /\bclass\s+Application\s+<\s+Rails::Application\b/u
212710
+ );
212711
+ const hasApplicationController = context.hasAnyFileMatchingPattern(
212712
+ /(^|\/)app\/controllers\/.+\.rb$/u,
212713
+ /\bApplicationController\b/u
212714
+ );
212715
+ if (hasRailsGem) {
212716
+ strongSignals += 1;
212717
+ score += 48;
212718
+ evidence.push("Found Gemfile with rails gem");
212719
+ }
212720
+ if (hasBinRails) {
212721
+ strongSignals += 1;
212722
+ score += 18;
212723
+ evidence.push("Found bin/rails");
212724
+ }
212725
+ if (hasRoutes) {
212726
+ mediumSignals += 1;
212727
+ score += 10;
212728
+ evidence.push("Found config/routes.rb");
212729
+ }
212730
+ if (hasControllers) {
212731
+ mediumSignals += 1;
212732
+ score += 8;
212733
+ evidence.push("Found app/controllers directory");
212734
+ }
212735
+ if (hasModels) {
212736
+ mediumSignals += 1;
212737
+ score += 6;
212738
+ evidence.push("Found app/models directory");
212739
+ }
212740
+ if (hasServices) {
212741
+ mediumSignals += 1;
212742
+ score += 5;
212743
+ evidence.push("Found app/services directory");
212744
+ }
212745
+ if (hasRailsRoutesDraw) {
212746
+ mediumSignals += 1;
212747
+ score += 8;
212748
+ evidence.push("Found Rails routes draw block");
212749
+ }
212750
+ if (hasApplicationClass) {
212751
+ mediumSignals += 1;
212752
+ score += 8;
212753
+ evidence.push("Found Rails application class");
212754
+ }
212755
+ if (hasApplicationController) {
212756
+ mediumSignals += 1;
212757
+ score += 5;
212758
+ evidence.push("Found ApplicationController inheritance");
212759
+ }
212760
+ if (!hasRailsGem || !(hasBinRails || hasRoutes || hasControllers || hasApplicationClass)) {
212761
+ return null;
212762
+ }
212763
+ return toResult({
212764
+ adapterId: "ruby_rails",
212765
+ confidence: strongSignals >= 2 || mediumSignals >= 3 ? "high" : "medium",
212766
+ score,
212767
+ evidence,
212768
+ projectKindHints: ["backend"],
212769
+ apiStyleHint: hasRoutes || hasControllers ? "rest" : "unknown"
212770
+ });
212771
+ },
212772
+ resolveProjectKinds: () => ["backend"],
212773
+ getImportExtractionMode: () => "generic"
212774
+ }),
212775
+ kotlin: buildAdapter({
212776
+ id: "kotlin",
212777
+ displayName: canonicalStackLabels.kotlin,
212778
+ ecosystem: "jvm",
212779
+ category: "generic-language",
212780
+ priority: 40,
212781
+ manifestDetectionHints: ["build.gradle.kts", "pom.xml", "settings.gradle.kts"],
212782
+ buildToolHints: ["gradle", "maven"],
212783
+ defaults: {
212784
+ sourceRoots: ["src/main/kotlin", "src"],
212785
+ moduleRootCandidates: ["src/main/kotlin", "src"],
212786
+ apiRoots: ["src/main/kotlin"],
212787
+ databaseRoots: ["src/main/resources/db", "src/main/resources/migrations"],
212788
+ configFiles: ["build.gradle.kts", "pom.xml", "settings.gradle.kts"],
212789
+ fileExtensions: [".kt", ".kts"],
212790
+ dependencyFileExtensions: [".kt", ".kts"],
212791
+ ignoreDirectories: [
212792
+ "target",
212793
+ "build",
212794
+ ".gradle",
212795
+ ".idea",
212796
+ "out",
212797
+ ...commonIgnoreDirectories
212798
+ ],
212799
+ entrypointHints: ["Application.kt", "Main.kt"],
212800
+ publicEntrypointDirectories: [],
212801
+ moduleIndexFileName: "Application.kt"
212802
+ },
212803
+ detect: (context) => {
212804
+ const evidence = [];
212805
+ let score = 0;
212806
+ const hasBuildGradleKts = context.hasFile("build.gradle.kts");
212807
+ const hasSettingsGradleKts = context.hasFile("settings.gradle.kts");
212808
+ const hasKotlinPluginInGradle = context.hasFileMatchingPattern("build.gradle.kts", /\bkotlin\s*\(\s*["']jvm["']\s*\)/u) || context.hasFileMatchingPattern("build.gradle.kts", /\bid\s*\(\s*["']org\.jetbrains\.kotlin\.jvm["']\s*\)/u);
212809
+ const hasPomKotlinPlugin = context.hasFileMatchingPattern("pom.xml", /\bkotlin-maven-plugin\b/u) || context.hasFileMatchingPattern("pom.xml", /\borg\.jetbrains\.kotlin\b/u);
212810
+ const hasKotlinSourceRoot = context.hasDirectory("src/main/kotlin");
212811
+ const hasKtFiles = context.hasFileExtension(".kt") || context.hasFileExtension(".kts");
212812
+ const hasKotlinPackageShape = context.hasDirectory("src/main/kotlin/com") || context.hasDirectory("src/main/kotlin/org");
212813
+ if (hasBuildGradleKts) {
212814
+ score += 30;
212815
+ evidence.push("Found build.gradle.kts");
212816
+ }
212817
+ if (hasSettingsGradleKts) {
212818
+ score += 8;
212819
+ evidence.push("Found settings.gradle.kts");
212820
+ }
212821
+ if (hasKotlinPluginInGradle) {
212822
+ score += 32;
212823
+ evidence.push("Found Kotlin JVM plugin in build.gradle.kts");
212824
+ }
212825
+ if (hasPomKotlinPlugin) {
212826
+ score += 32;
212827
+ evidence.push("Found Kotlin Maven plugin markers in pom.xml");
212828
+ }
212829
+ if (hasKotlinSourceRoot) {
212830
+ score += 18;
212831
+ evidence.push("Found src/main/kotlin source root");
212832
+ }
212833
+ if (hasKtFiles) {
212834
+ score += 16;
212835
+ evidence.push("Found Kotlin source files");
212836
+ }
212837
+ if (hasKotlinPackageShape) {
212838
+ score += 4;
212839
+ evidence.push("Found conventional Kotlin package directory under src/main/kotlin");
212840
+ }
212841
+ const hasSafeKotlinEvidence = hasKtFiles && (hasBuildGradleKts || hasKotlinPluginInGradle || hasPomKotlinPlugin || hasKotlinSourceRoot);
212842
+ if (!hasSafeKotlinEvidence) {
212843
+ return null;
212844
+ }
212845
+ return toResult({
212846
+ adapterId: "kotlin",
212847
+ confidence: (hasKotlinPluginInGradle || hasPomKotlinPlugin) && hasKotlinSourceRoot ? "high" : hasBuildGradleKts || hasKotlinSourceRoot ? "medium" : "low",
212848
+ score,
212849
+ evidence,
212850
+ projectKindHints: ["backend", "library"],
212851
+ apiStyleHint: "unknown"
212852
+ });
212853
+ },
212854
+ resolveProjectKinds: () => ["backend", "library"],
212498
212855
  getImportExtractionMode: () => "generic"
212499
212856
  }),
212500
212857
  kotlin_spring: buildAdapter({
@@ -212750,7 +213107,7 @@ var stackAdaptersById = {
212750
213107
  let score = 0;
212751
213108
  if (hasVueDependency) {
212752
213109
  strongSignals += 1;
212753
- score += 28;
213110
+ score += 30;
212754
213111
  evidence.push("Found package.json with vue dependency");
212755
213112
  }
212756
213113
  if (hasAppVue) {
@@ -212765,12 +213122,12 @@ var stackAdaptersById = {
212765
213122
  }
212766
213123
  if (hasVitePluginInConfig) {
212767
213124
  strongSignals += 1;
212768
- score += 20;
213125
+ score += 24;
212769
213126
  evidence.push("Found Vue plugin usage in vite.config.ts or vite.config.js");
212770
213127
  }
212771
213128
  if (hasVitePluginDependency) {
212772
213129
  strongSignals += 1;
212773
- score += 18;
213130
+ score += 22;
212774
213131
  evidence.push("Found package.json with @vitejs/plugin-vue dependency");
212775
213132
  }
212776
213133
  if (hasSrcViews) {
@@ -212785,12 +213142,12 @@ var stackAdaptersById = {
212785
213142
  }
212786
213143
  if (hasCreateAppReference) {
212787
213144
  mediumSignals += 1;
212788
- score += 10;
213145
+ score += 12;
212789
213146
  evidence.push("Found createApp usage in src/main.ts or src/main.js");
212790
213147
  }
212791
213148
  if (hasViteConfig && hasVueDependency) {
212792
213149
  mediumSignals += 1;
212793
- score += 6;
213150
+ score += 8;
212794
213151
  evidence.push("Found vite.config.ts or vite.config.js with Vue package evidence");
212795
213152
  }
212796
213153
  if (hasTsConfigApp) {
@@ -213118,7 +213475,264 @@ async function listModuleDirectoryNames(workspaceRoot, modulesRootRelativePath =
213118
213475
  const entries = await import_node_fs.promises.readdir(modulesRoot, { withFileTypes: true });
213119
213476
  return entries.filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name).sort((left, right) => left.localeCompare(right));
213120
213477
  }
213478
+ var ignoredModuleDirectoryNames = /* @__PURE__ */ new Set([
213479
+ ".git",
213480
+ ".archpilot",
213481
+ ".vscode",
213482
+ ".vscode-test",
213483
+ "node_modules",
213484
+ "dist",
213485
+ "build",
213486
+ "out",
213487
+ "coverage",
213488
+ ".next",
213489
+ "generated",
213490
+ "target",
213491
+ "vendor",
213492
+ ".venv",
213493
+ "venv",
213494
+ "__pycache__"
213495
+ ]);
213496
+ var noisyModuleDirectoryNames = /* @__PURE__ */ new Set([
213497
+ "components",
213498
+ "hooks",
213499
+ "utils",
213500
+ "util",
213501
+ "lib",
213502
+ "common",
213503
+ "shared",
213504
+ "shared-ui",
213505
+ "types",
213506
+ "constants",
213507
+ "config",
213508
+ "test",
213509
+ "tests",
213510
+ "__tests__",
213511
+ "fixtures",
213512
+ "mocks",
213513
+ "__mocks__",
213514
+ "generated",
213515
+ "dist",
213516
+ "build",
213517
+ "out",
213518
+ "node_modules"
213519
+ ]);
213520
+ var sourceFileExtensions = /* @__PURE__ */ new Set([
213521
+ ".ts",
213522
+ ".tsx",
213523
+ ".js",
213524
+ ".jsx",
213525
+ ".vue",
213526
+ ".java",
213527
+ ".py",
213528
+ ".php",
213529
+ ".go",
213530
+ ".cs",
213531
+ ".rb",
213532
+ ".kt",
213533
+ ".swift",
213534
+ ".rs"
213535
+ ]);
213536
+ var publicEntrypointNames = /* @__PURE__ */ new Set([
213537
+ "index.ts",
213538
+ "index.tsx",
213539
+ "index.js",
213540
+ "index.jsx",
213541
+ "index.vue",
213542
+ "__init__.py",
213543
+ "main.go",
213544
+ "mod.ts",
213545
+ "mod.js"
213546
+ ]);
213547
+ function isIgnoredDirectoryName(directoryName) {
213548
+ return ignoredModuleDirectoryNames.has(directoryName.toLowerCase());
213549
+ }
213550
+ function isNoisyModuleName(moduleName) {
213551
+ return noisyModuleDirectoryNames.has(moduleName.toLowerCase());
213552
+ }
213553
+ function toPathSegments(relativePath) {
213554
+ return normalizePath(relativePath).split("/").filter((segment) => segment.length > 0);
213555
+ }
213556
+ function toModuleIdFromPath(relativePath, fallbackRoot) {
213557
+ const segments = toPathSegments(relativePath);
213558
+ const lowerSegments = segments.map((segment) => segment.toLowerCase());
213559
+ const sourceIndex = lowerSegments.lastIndexOf("src");
213560
+ if (sourceIndex >= 0 && sourceIndex + 2 < segments.length) {
213561
+ const parent = segments[sourceIndex - 1];
213562
+ const container = lowerSegments[sourceIndex + 1];
213563
+ if (["modules", "features"].includes(container)) {
213564
+ return [parent, segments[sourceIndex + 2]].filter(Boolean).join("/");
213565
+ }
213566
+ if (["domain", "domains", "application", "infrastructure"].includes(container)) {
213567
+ return [segments[sourceIndex + 1], segments[sourceIndex + 2]].join("/");
213568
+ }
213569
+ return [parent, segments[sourceIndex + 1]].filter(Boolean).join("/");
213570
+ }
213571
+ if (sourceIndex >= 0 && sourceIndex + 1 < segments.length) {
213572
+ const parent = segments[sourceIndex - 1];
213573
+ return [parent, segments[sourceIndex + 1]].filter(Boolean).join("/");
213574
+ }
213575
+ if (segments[0] === "src" && segments.length >= 3) {
213576
+ return `${segments[1]}/${segments[2]}`;
213577
+ }
213578
+ if (fallbackRoot) {
213579
+ const rootSegments = toPathSegments(fallbackRoot);
213580
+ const relativeSegments = segments.slice(rootSegments.length);
213581
+ if (relativeSegments.length > 0) {
213582
+ return relativeSegments.join("/");
213583
+ }
213584
+ }
213585
+ return segments.slice(-1)[0] ?? relativePath;
213586
+ }
213587
+ function isKnownNestedModuleRoot(relativePath) {
213588
+ const segments = toPathSegments(relativePath).map((segment) => segment.toLowerCase());
213589
+ if (segments.length < 2) {
213590
+ return false;
213591
+ }
213592
+ const lastParent = segments[segments.length - 2];
213593
+ if (["modules", "features", "domain", "domains", "application", "infrastructure"].includes(lastParent)) {
213594
+ return true;
213595
+ }
213596
+ if (segments.length >= 3 && segments[segments.length - 3] === "src") {
213597
+ return true;
213598
+ }
213599
+ return false;
213600
+ }
213601
+ async function inspectPotentialModuleRoot(absolutePath) {
213602
+ let sourceFileCount = 0;
213603
+ let hasPublicEntrypoint = false;
213604
+ const queue = [{ absolutePath, depth: 0 }];
213605
+ while (queue.length > 0) {
213606
+ const current = queue.shift();
213607
+ if (!current) {
213608
+ continue;
213609
+ }
213610
+ let entries;
213611
+ try {
213612
+ entries = await import_node_fs.promises.readdir(current.absolutePath, { withFileTypes: true });
213613
+ } catch {
213614
+ continue;
213615
+ }
213616
+ for (const entry of entries) {
213617
+ const entryPath = path.join(current.absolutePath, entry.name);
213618
+ if (entry.isDirectory()) {
213619
+ if (current.depth >= 2 || isIgnoredDirectoryName(entry.name)) {
213620
+ continue;
213621
+ }
213622
+ queue.push({ absolutePath: entryPath, depth: current.depth + 1 });
213623
+ continue;
213624
+ }
213625
+ if (!entry.isFile()) {
213626
+ continue;
213627
+ }
213628
+ if (sourceFileExtensions.has(path.extname(entry.name).toLowerCase())) {
213629
+ sourceFileCount += 1;
213630
+ }
213631
+ if (current.depth === 0 && publicEntrypointNames.has(entry.name.toLowerCase())) {
213632
+ hasPublicEntrypoint = true;
213633
+ }
213634
+ }
213635
+ }
213636
+ return { sourceFileCount, hasPublicEntrypoint };
213637
+ }
213638
+ async function addModuleIfStrongCandidate(workspaceRoot, discovered, sourcePath, moduleId, options) {
213639
+ const normalizedSourcePath = normalizePath(sourcePath);
213640
+ const name = normalizedSourcePath.split("/").pop() ?? moduleId;
213641
+ if (options?.allowNoisyNames !== true && isNoisyModuleName(name)) {
213642
+ return;
213643
+ }
213644
+ const absolutePath = path.join(workspaceRoot, ...normalizedSourcePath.split("/"));
213645
+ const inspected = await inspectPotentialModuleRoot(absolutePath);
213646
+ const hasStrongEvidence = inspected.sourceFileCount >= 2 || inspected.hasPublicEntrypoint || isKnownNestedModuleRoot(normalizedSourcePath);
213647
+ if (options?.requireStrongEvidence !== false && !hasStrongEvidence) {
213648
+ return;
213649
+ }
213650
+ const normalizedModuleId = normalizePath(moduleId || toModuleIdFromPath(normalizedSourcePath, options?.fallbackRoot));
213651
+ if (!normalizedModuleId || options?.allowNoisyNames !== true && isNoisyModuleName(normalizedModuleId.split("/").pop() ?? normalizedModuleId)) {
213652
+ return;
213653
+ }
213654
+ if (!discovered.has(normalizedModuleId)) {
213655
+ discovered.set(normalizedModuleId, {
213656
+ moduleId: normalizedModuleId,
213657
+ sourcePath: normalizedSourcePath
213658
+ });
213659
+ }
213660
+ }
213661
+ async function addChildrenFromRoot(workspaceRoot, discovered, rootRelativePath, options) {
213662
+ const normalizedRoot = normalizePath(rootRelativePath);
213663
+ const absoluteRoot = path.join(workspaceRoot, ...normalizedRoot.split("/"));
213664
+ if (!await pathExists(absoluteRoot)) {
213665
+ return;
213666
+ }
213667
+ const entries = (await import_node_fs.promises.readdir(absoluteRoot, { withFileTypes: true })).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".") && !isIgnoredDirectoryName(entry.name)).sort((left, right) => left.name.localeCompare(right.name));
213668
+ for (const entry of entries) {
213669
+ const sourcePath = `${normalizedRoot}/${entry.name}`;
213670
+ const moduleId = options?.includeParentPrefix && options.parentId ? `${options.parentId}/${entry.name}` : toModuleIdFromPath(sourcePath, normalizedRoot);
213671
+ await addModuleIfStrongCandidate(workspaceRoot, discovered, sourcePath, moduleId, {
213672
+ fallbackRoot: normalizedRoot,
213673
+ ...options?.requireStrongEvidence !== void 0 ? { requireStrongEvidence: options.requireStrongEvidence } : {},
213674
+ ...options?.allowNoisyNames !== void 0 ? { allowNoisyNames: options.allowNoisyNames } : {}
213675
+ });
213676
+ }
213677
+ }
213678
+ async function addMonorepoContainerModules(workspaceRoot, discovered, container) {
213679
+ const absoluteContainer = path.join(workspaceRoot, container);
213680
+ if (!await pathExists(absoluteContainer)) {
213681
+ return;
213682
+ }
213683
+ const entries = (await import_node_fs.promises.readdir(absoluteContainer, { withFileTypes: true })).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".") && !isIgnoredDirectoryName(entry.name)).sort((left, right) => left.name.localeCompare(right.name));
213684
+ for (const entry of entries) {
213685
+ const packagePath = `${container}/${entry.name}`;
213686
+ await addModuleIfStrongCandidate(workspaceRoot, discovered, packagePath, entry.name, {
213687
+ requireStrongEvidence: false
213688
+ });
213689
+ for (const sourceRoot of [
213690
+ `${packagePath}/src`,
213691
+ `${packagePath}/src/modules`,
213692
+ `${packagePath}/src/features`,
213693
+ `${packagePath}/src/domain`,
213694
+ `${packagePath}/src/domains`,
213695
+ `${packagePath}/src/application`,
213696
+ `${packagePath}/src/infrastructure`
213697
+ ]) {
213698
+ await addChildrenFromRoot(workspaceRoot, discovered, sourceRoot, {
213699
+ parentId: entry.name,
213700
+ includeParentPrefix: true
213701
+ });
213702
+ }
213703
+ }
213704
+ }
213705
+ async function discoverHierarchicalModuleRoots(workspaceRoot, modulesRootRelativePath = "src/modules") {
213706
+ const discovered = /* @__PURE__ */ new Map();
213707
+ await addChildrenFromRoot(workspaceRoot, discovered, normalizePath(modulesRootRelativePath), {
213708
+ requireStrongEvidence: false,
213709
+ allowNoisyNames: normalizePath(modulesRootRelativePath) === "src"
213710
+ });
213711
+ for (const root of [
213712
+ "src/modules",
213713
+ "src/features",
213714
+ "src/domain",
213715
+ "src/domains",
213716
+ "src/application",
213717
+ "src/infrastructure"
213718
+ ]) {
213719
+ await addChildrenFromRoot(workspaceRoot, discovered, root);
213720
+ }
213721
+ for (const container of ["packages", "apps", "services"]) {
213722
+ await addMonorepoContainerModules(workspaceRoot, discovered, container);
213723
+ }
213724
+ return [...discovered.values()].sort((left, right) => left.moduleId.localeCompare(right.moduleId));
213725
+ }
213121
213726
  async function discoverModuleRoots(workspaceRoot, modulesRootRelativePath = "src/modules") {
213727
+ const hierarchicalModules = await discoverHierarchicalModuleRoots(workspaceRoot, modulesRootRelativePath);
213728
+ if (hierarchicalModules.length > 0) {
213729
+ return new Map(
213730
+ hierarchicalModules.map((entry) => [
213731
+ entry.moduleId,
213732
+ path.join(workspaceRoot, ...entry.sourcePath.split("/"))
213733
+ ])
213734
+ );
213735
+ }
213122
213736
  const moduleDirectories = await listModuleDirectoryNames(workspaceRoot, modulesRootRelativePath);
213123
213737
  const modulesRoot = path.join(workspaceRoot, ...modulesRootRelativePath.split("/"));
213124
213738
  return new Map(
@@ -214497,7 +215111,8 @@ function buildArchitectureContract(base, overrides) {
214497
215111
  }
214498
215112
  function buildDefaultModuleContractPath(moduleName, contract) {
214499
215113
  const contractsRoot = resolveContractsRootFromContract(contract);
214500
- return `${contractsRoot}/${moduleName}.contract.json`;
215114
+ const safeModuleName = moduleName.replace(/\\/g, "/").split("/").map((segment) => segment.trim()).filter((segment) => segment.length > 0).join(".");
215115
+ return `${contractsRoot}/${safeModuleName}.contract.json`;
214501
215116
  }
214502
215117
  async function loadArchitectureContract(workspaceRoot) {
214503
215118
  const configPath = path7.join(workspaceRoot, ".archpilot", "architecture.json");
@@ -214628,8 +215243,63 @@ var DependencyRulesConfigError = class extends Error {
214628
215243
  function normalizePath2(filePath) {
214629
215244
  return filePath.replace(/\\/g, "/");
214630
215245
  }
214631
- function getModulesRoot(workspaceRoot, modulesRootRelativePath = "src/modules") {
214632
- return path8.join(workspaceRoot, ...modulesRootRelativePath.split("/"));
215246
+ function toSafeContractModuleFileStem(moduleName) {
215247
+ return normalizePath2(moduleName).split("/").map((segment) => segment.trim()).filter((segment) => segment.length > 0).join(".");
215248
+ }
215249
+ function normalizeAbsolutePath(filePath) {
215250
+ return normalizePath2(path8.resolve(filePath)).toLowerCase();
215251
+ }
215252
+ function isPathInsideOrEqual(childPath, parentPath) {
215253
+ const child = normalizeAbsolutePath(childPath);
215254
+ const parent = normalizeAbsolutePath(parentPath);
215255
+ return child === parent || child.startsWith(`${parent}/`);
215256
+ }
215257
+ function isParentChildModuleRelationship(leftModule, rightModule) {
215258
+ if (leftModule === rightModule) {
215259
+ return false;
215260
+ }
215261
+ return leftModule.startsWith(`${rightModule}/`) || rightModule.startsWith(`${leftModule}/`);
215262
+ }
215263
+ function isPeerModuleImport(dependencyImport) {
215264
+ return !isParentChildModuleRelationship(
215265
+ dependencyImport.sourceModule,
215266
+ dependencyImport.targetModule
215267
+ );
215268
+ }
215269
+ function filterPeerModuleImports(imports) {
215270
+ return imports.filter(isPeerModuleImport);
215271
+ }
215272
+ function buildModuleHierarchyEdges(moduleIds) {
215273
+ const moduleIdSet = new Set(moduleIds);
215274
+ const edges = [];
215275
+ for (const moduleId of moduleIds) {
215276
+ const parentModule = moduleId.split("/").slice(0, -1).map((_, index, segments) => segments.slice(0, segments.length - index).join("/")).find((candidate) => moduleIdSet.has(candidate));
215277
+ if (!parentModule) {
215278
+ continue;
215279
+ }
215280
+ edges.push({ parentModule, childModule: moduleId });
215281
+ }
215282
+ return edges.sort((left, right) => {
215283
+ const parentCompare = left.parentModule.localeCompare(right.parentModule);
215284
+ if (parentCompare !== 0) {
215285
+ return parentCompare;
215286
+ }
215287
+ return left.childModule.localeCompare(right.childModule);
215288
+ });
215289
+ }
215290
+ function buildChildModulesByParent(hierarchyEdges) {
215291
+ const byParent = /* @__PURE__ */ new Map();
215292
+ for (const edge of hierarchyEdges) {
215293
+ const children = byParent.get(edge.parentModule) ?? /* @__PURE__ */ new Set();
215294
+ children.add(edge.childModule);
215295
+ byParent.set(edge.parentModule, children);
215296
+ }
215297
+ return new Map(
215298
+ [...byParent.entries()].map(([parentModule, childModules]) => [
215299
+ parentModule,
215300
+ sortUnique2(childModules)
215301
+ ])
215302
+ );
214633
215303
  }
214634
215304
  function getScriptKind(filePath) {
214635
215305
  const extension = path8.extname(filePath).toLowerCase();
@@ -214670,6 +215340,37 @@ async function readTextFileIfExists2(filePath) {
214670
215340
  return void 0;
214671
215341
  }
214672
215342
  }
215343
+ async function readPackageName(packageJsonPath) {
215344
+ const contents = await readTextFileIfExists2(packageJsonPath);
215345
+ if (!contents) {
215346
+ return void 0;
215347
+ }
215348
+ try {
215349
+ const parsed = JSON.parse(contents);
215350
+ return typeof parsed.name === "string" && parsed.name.trim().length > 0 ? parsed.name.trim() : void 0;
215351
+ } catch {
215352
+ return void 0;
215353
+ }
215354
+ }
215355
+ async function buildWorkspacePackageAliasMap(moduleRoots) {
215356
+ const aliases = [];
215357
+ for (const [moduleId, moduleRoot] of [...moduleRoots.entries()].sort(
215358
+ ([left], [right]) => left.localeCompare(right)
215359
+ )) {
215360
+ const packageName = await readPackageName(path8.join(moduleRoot, "package.json"));
215361
+ if (!packageName) {
215362
+ continue;
215363
+ }
215364
+ aliases.push({ packageName, moduleId, moduleRoot });
215365
+ }
215366
+ return aliases.sort((left, right) => {
215367
+ const lengthCompare = right.packageName.length - left.packageName.length;
215368
+ if (lengthCompare !== 0) {
215369
+ return lengthCompare;
215370
+ }
215371
+ return left.packageName.localeCompare(right.packageName);
215372
+ });
215373
+ }
214673
215374
  function validateDependencyModuleRules(moduleName, value) {
214674
215375
  if (!value || typeof value !== "object" || Array.isArray(value)) {
214675
215376
  throw new DependencyRulesConfigError(
@@ -215143,7 +215844,32 @@ async function resolveImportPath(basePath, adapter) {
215143
215844
  }
215144
215845
  return void 0;
215145
215846
  }
215146
- async function resolveCrossModuleImport(workspaceRoot, modulesRootRelativePath, moduleRoots, explicitStandaloneModuleByPath, sourceFilePath, importSpecifier, adapter) {
215847
+ function resolvePackageAliasRemainder(packageName, importSpecifier) {
215848
+ if (importSpecifier === packageName) {
215849
+ return "";
215850
+ }
215851
+ if (importSpecifier.startsWith(`${packageName}/`)) {
215852
+ return importSpecifier.slice(packageName.length + 1);
215853
+ }
215854
+ return void 0;
215855
+ }
215856
+ async function resolveWorkspacePackageImportPath(importSpecifier, workspacePackageAliases, adapter) {
215857
+ const packageAlias = workspacePackageAliases.map((alias2) => ({
215858
+ alias: alias2,
215859
+ remainder: resolvePackageAliasRemainder(alias2.packageName, importSpecifier)
215860
+ })).find(
215861
+ (candidate) => candidate.remainder !== void 0
215862
+ );
215863
+ if (!packageAlias) {
215864
+ return void 0;
215865
+ }
215866
+ const { alias, remainder } = packageAlias;
215867
+ if (remainder.length === 0) {
215868
+ return resolveImportPath(alias.moduleRoot, adapter);
215869
+ }
215870
+ return void 0;
215871
+ }
215872
+ async function resolveCrossModuleImport(workspaceRoot, modulesRootRelativePath, moduleRoots, explicitStandaloneModuleByPath, workspacePackageAliases, sourceFilePath, importSpecifier, adapter) {
215147
215873
  let basePath;
215148
215874
  const normalizedModulesRoot = normalizePath2(modulesRootRelativePath).replace(/^\/+/, "");
215149
215875
  if (usesJavaImportParser(adapter) && !importSpecifier.startsWith(".")) {
@@ -215198,6 +215924,22 @@ async function resolveCrossModuleImport(workspaceRoot, modulesRootRelativePath,
215198
215924
  basePath = path8.join(workspaceRoot, importSpecifier.slice(1));
215199
215925
  } else if (normalizedModulesRoot.startsWith("src/") && importSpecifier.startsWith(`${normalizedModulesRoot.slice("src/".length)}/`)) {
215200
215926
  basePath = path8.join(workspaceRoot, "src", importSpecifier);
215927
+ } else {
215928
+ const workspacePackageImportPath = await resolveWorkspacePackageImportPath(
215929
+ importSpecifier,
215930
+ workspacePackageAliases,
215931
+ adapter
215932
+ );
215933
+ if (workspacePackageImportPath) {
215934
+ basePath = workspacePackageImportPath;
215935
+ } else {
215936
+ const moduleImportTarget = [...moduleRoots.entries()].filter(([moduleId]) => importSpecifier === moduleId || importSpecifier.startsWith(`${moduleId}/`)).sort((left, right) => right[0].length - left[0].length || left[0].localeCompare(right[0]))[0];
215937
+ if (moduleImportTarget) {
215938
+ const [moduleId, moduleRoot] = moduleImportTarget;
215939
+ const remainder = importSpecifier === moduleId ? "" : importSpecifier.slice(moduleId.length + 1);
215940
+ basePath = path8.join(moduleRoot, ...remainder.split("/").filter((segment) => segment.length > 0));
215941
+ }
215942
+ }
215201
215943
  }
215202
215944
  if (!basePath) {
215203
215945
  return void 0;
@@ -215206,17 +215948,8 @@ async function resolveCrossModuleImport(workspaceRoot, modulesRootRelativePath,
215206
215948
  if (!resolvedPath) {
215207
215949
  return void 0;
215208
215950
  }
215209
- const modulesRoot = getModulesRoot(workspaceRoot, modulesRootRelativePath);
215210
- const relativeToModulesRoot = normalizePath2(path8.relative(modulesRoot, resolvedPath));
215211
- if (relativeToModulesRoot === "" || relativeToModulesRoot.startsWith("../") || relativeToModulesRoot.startsWith("..\\")) {
215212
- return void 0;
215213
- }
215214
- const segments = relativeToModulesRoot.split("/");
215215
- if (segments.length === 0) {
215216
- return void 0;
215217
- }
215218
- const targetModule = segments[0];
215219
- if (!moduleRoots.has(targetModule)) {
215951
+ const targetModuleEntry = [...moduleRoots.entries()].filter(([, moduleRoot]) => isPathInsideOrEqual(resolvedPath, moduleRoot)).sort((left, right) => right[1].length - left[1].length || left[0].localeCompare(right[0]))[0];
215952
+ if (!targetModuleEntry) {
215220
215953
  const standaloneTargetModule = explicitStandaloneModuleByPath.get(
215221
215954
  normalizePath2(path8.relative(workspaceRoot, resolvedPath))
215222
215955
  );
@@ -215229,7 +215962,11 @@ async function resolveCrossModuleImport(workspaceRoot, modulesRootRelativePath,
215229
215962
  isPublicImport: true
215230
215963
  };
215231
215964
  }
215232
- const targetSubPath = segments.slice(1).join("/");
215965
+ const [targetModule, targetModuleRoot] = targetModuleEntry;
215966
+ const targetSubPath = normalizePath2(path8.relative(targetModuleRoot, resolvedPath));
215967
+ if (targetSubPath === "" || targetSubPath.startsWith("../") || targetSubPath.startsWith("..\\")) {
215968
+ return void 0;
215969
+ }
215233
215970
  const indexEntrypointCandidates = adapter.dependencyParsingFileExtensions.map(
215234
215971
  (extension) => `index${extension}`
215235
215972
  );
@@ -215242,7 +215979,7 @@ async function resolveCrossModuleImport(workspaceRoot, modulesRootRelativePath,
215242
215979
  isPublicImport
215243
215980
  };
215244
215981
  }
215245
- async function collectCrossModuleImports(workspaceRoot, modulesRootRelativePath, moduleRoots, explicitStandaloneModuleByPath, adapter, options) {
215982
+ async function collectCrossModuleImports(workspaceRoot, modulesRootRelativePath, moduleRoots, explicitStandaloneModuleByPath, workspacePackageAliases, adapter, options) {
215246
215983
  const imports = [];
215247
215984
  const includeTestFiles = options?.includeTestFiles === true;
215248
215985
  if (!supportsDependencyImportParsing(adapter)) {
@@ -215251,7 +215988,10 @@ async function collectCrossModuleImports(workspaceRoot, modulesRootRelativePath,
215251
215988
  for (const [moduleName, moduleRoot] of [...moduleRoots.entries()].sort(
215252
215989
  ([left], [right]) => left.localeCompare(right)
215253
215990
  )) {
215254
- const sourceFiles = await collectSourceFiles(moduleRoot, adapter);
215991
+ const childModuleRoots = [...moduleRoots.entries()].filter(([childModuleName, childModuleRoot]) => childModuleName !== moduleName && isPathInsideOrEqual(childModuleRoot, moduleRoot)).map(([, childModuleRoot]) => childModuleRoot);
215992
+ const sourceFiles = (await collectSourceFiles(moduleRoot, adapter)).filter(
215993
+ (sourceFile) => !childModuleRoots.some((childModuleRoot) => isPathInsideOrEqual(sourceFile, childModuleRoot))
215994
+ );
215255
215995
  for (const sourceFilePath of sourceFiles) {
215256
215996
  const sourceFileRelativePath = normalizePath2(
215257
215997
  path8.relative(workspaceRoot, sourceFilePath)
@@ -215274,6 +216014,7 @@ async function collectCrossModuleImports(workspaceRoot, modulesRootRelativePath,
215274
216014
  modulesRootRelativePath,
215275
216015
  moduleRoots,
215276
216016
  explicitStandaloneModuleByPath,
216017
+ workspacePackageAliases,
215277
216018
  sourceFilePath,
215278
216019
  importReference.specifier,
215279
216020
  adapter
@@ -215419,7 +216160,7 @@ async function loadModuleArchitectureContract(workspaceRoot, architectureContrac
215419
216160
  const registryEntry = architectureContract.modules[moduleName];
215420
216161
  const contractPath = registryEntry?.contract ?? path8.posix.join(
215421
216162
  resolveContractsRootFromContract(architectureContract),
215422
- `${moduleName}.contract.json`
216163
+ `${toSafeContractModuleFileStem(moduleName)}.contract.json`
215423
216164
  );
215424
216165
  const fullContractPath = path8.join(workspaceRoot, ...contractPath.split("/"));
215425
216166
  const content = await readTextFileIfExists2(fullContractPath);
@@ -215566,6 +216307,7 @@ async function buildDependencyGraphSummary(workspaceRoot, architectureContract,
215566
216307
  workspaceRoot,
215567
216308
  architectureContract
215568
216309
  );
216310
+ const workspacePackageAliases = await buildWorkspacePackageAliasMap(moduleRoots);
215569
216311
  const moduleIds = sortUnique2([...moduleRoots.keys(), ...explicitStandaloneModuleByPath.values()]);
215570
216312
  const registeredModuleIds = new Set(moduleIds);
215571
216313
  const imports = await collectCrossModuleImports(
@@ -215573,9 +216315,11 @@ async function buildDependencyGraphSummary(workspaceRoot, architectureContract,
215573
216315
  modulesRoot,
215574
216316
  moduleRoots,
215575
216317
  explicitStandaloneModuleByPath,
216318
+ workspacePackageAliases,
215576
216319
  adapter
215577
216320
  );
215578
- const actualBySource = buildActualImportedModuleSetBySource(imports, registeredModuleIds);
216321
+ const peerImports = filterPeerModuleImports(imports);
216322
+ const actualBySource = buildActualImportedModuleSetBySource(peerImports, registeredModuleIds);
215579
216323
  const moduleContractCache = /* @__PURE__ */ new Map();
215580
216324
  const declaredBySource = /* @__PURE__ */ new Map();
215581
216325
  const hasContractByModule = /* @__PURE__ */ new Map();
@@ -215593,7 +216337,7 @@ async function buildDependencyGraphSummary(workspaceRoot, architectureContract,
215593
216337
  new Set(
215594
216338
  sortUnique2(
215595
216339
  sourceContract.contract.dependsOn.filter(
215596
- (dependencyModule) => registeredModuleIds.has(dependencyModule)
216340
+ (dependencyModule) => registeredModuleIds.has(dependencyModule) && !isParentChildModuleRelationship(moduleId, dependencyModule)
215597
216341
  )
215598
216342
  )
215599
216343
  )
@@ -215605,6 +216349,11 @@ async function buildDependencyGraphSummary(workspaceRoot, architectureContract,
215605
216349
  }
215606
216350
  const inboundDeclared = buildInboundDependencyMap(declaredBySource, moduleIds);
215607
216351
  const inboundActual = buildInboundDependencyMap(actualBySource, moduleIds);
216352
+ const hierarchyEdges = buildModuleHierarchyEdges(moduleIds);
216353
+ const childModulesByParent = buildChildModulesByParent(hierarchyEdges);
216354
+ const parentModuleByChild = new Map(
216355
+ hierarchyEdges.map((edge) => [edge.childModule, edge.parentModule])
216356
+ );
215608
216357
  return {
215609
216358
  modules: moduleIds.map((moduleId) => {
215610
216359
  const declaredDependencies = sortUnique2(declaredBySource.get(moduleId) ?? []);
@@ -215618,6 +216367,8 @@ async function buildDependencyGraphSummary(workspaceRoot, architectureContract,
215618
216367
  return {
215619
216368
  module: moduleId,
215620
216369
  hasContract: hasContractByModule.get(moduleId) ?? false,
216370
+ ...parentModuleByChild.has(moduleId) ? { parentModule: parentModuleByChild.get(moduleId) } : {},
216371
+ childModules: childModulesByParent.get(moduleId) ?? [],
215621
216372
  declaredDependencies,
215622
216373
  actualDependencies,
215623
216374
  inboundDeclaredFrom: inboundDeclared.get(moduleId) ?? [],
@@ -215625,7 +216376,8 @@ async function buildDependencyGraphSummary(workspaceRoot, architectureContract,
215625
216376
  unusedDeclaredDependencies,
215626
216377
  undeclaredActualDependencies
215627
216378
  };
215628
- })
216379
+ }),
216380
+ hierarchyEdges
215629
216381
  };
215630
216382
  }
215631
216383
  async function validateDependencyContractBoundaries(workspaceRoot, architectureContract, options) {
@@ -215639,19 +216391,22 @@ async function validateDependencyContractBoundaries(workspaceRoot, architectureC
215639
216391
  workspaceRoot,
215640
216392
  architectureContract
215641
216393
  );
216394
+ const workspacePackageAliases = await buildWorkspacePackageAliasMap(moduleRoots);
215642
216395
  const imports = await collectCrossModuleImports(
215643
216396
  workspaceRoot,
215644
216397
  modulesRoot,
215645
216398
  moduleRoots,
215646
216399
  explicitStandaloneModuleByPath,
216400
+ workspacePackageAliases,
215647
216401
  adapter
215648
216402
  );
216403
+ const peerImports = filterPeerModuleImports(imports);
215649
216404
  const findings = [];
215650
216405
  const moduleContractCache = /* @__PURE__ */ new Map();
215651
216406
  if (options.checkDeclaredDependencies) {
215652
216407
  const failures = [];
215653
216408
  const skippedFindings = /* @__PURE__ */ new Map();
215654
- for (const dependencyImport of imports) {
216409
+ for (const dependencyImport of peerImports) {
215655
216410
  const sourceContract = await loadModuleArchitectureContract(
215656
216411
  workspaceRoot,
215657
216412
  architectureContract,
@@ -215723,7 +216478,7 @@ async function validateDependencyContractBoundaries(workspaceRoot, architectureC
215723
216478
  }
215724
216479
  if (failures.length > 0) {
215725
216480
  findings.push(...failures);
215726
- } else if (imports.length === 0) {
216481
+ } else if (peerImports.length === 0) {
215727
216482
  findings.push({
215728
216483
  result: makeValidationResult(
215729
216484
  "AP-DEP-004",
@@ -215748,7 +216503,7 @@ async function validateDependencyContractBoundaries(workspaceRoot, architectureC
215748
216503
  if (options.checkPublicSurfaceImports) {
215749
216504
  const failures = [];
215750
216505
  const skippedFindings = /* @__PURE__ */ new Map();
215751
- for (const dependencyImport of imports) {
216506
+ for (const dependencyImport of peerImports) {
215752
216507
  const sourceContract = await loadModuleArchitectureContract(
215753
216508
  workspaceRoot,
215754
216509
  architectureContract,
@@ -215815,17 +216570,18 @@ async function validateDependencyContractBoundaries(workspaceRoot, architectureC
215815
216570
  }
215816
216571
  continue;
215817
216572
  }
215818
- if (targetContract.contract.publicEntrypoints.length > 0 && normalizePath2(dependencyImport.resolvedTargetRelativePath) !== buildModuleIndexEntrypointPath(
215819
- modulesRoot,
215820
- dependencyImport.targetModule,
215821
- adapter
216573
+ if (targetContract.contract.publicEntrypoints.length > 0 && !isImportWithinPublicEntrypoints(
216574
+ dependencyImport.resolvedTargetRelativePath,
216575
+ targetContract.contract.publicEntrypoints
215822
216576
  )) {
216577
+ const expectedEntrypoint = targetContract.contract.publicEntrypoints[0] ?? buildModuleIndexEntrypointPath(modulesRoot, dependencyImport.targetModule, adapter);
216578
+ const publicEntrypointLabel = expectedEntrypoint.endsWith("/index.ts") ? "index.ts public entrypoint" : "public entrypoint";
215823
216579
  failures.push({
215824
216580
  result: makeValidationResult(
215825
216581
  "AP-DEP-005",
215826
216582
  "error",
215827
216583
  false,
215828
- `Module '${dependencyImport.sourceModule}' must import module '${dependencyImport.targetModule}' via its index.ts public entrypoint: ${buildModuleIndexEntrypointPath(modulesRoot, dependencyImport.targetModule, adapter)}.`,
216584
+ `Module '${dependencyImport.sourceModule}' must import module '${dependencyImport.targetModule}' via its ${publicEntrypointLabel}: ${expectedEntrypoint}.`,
215829
216585
  {
215830
216586
  findingType: "module-dependency",
215831
216587
  module: dependencyImport.sourceModule,
@@ -215866,7 +216622,7 @@ async function validateDependencyContractBoundaries(workspaceRoot, architectureC
215866
216622
  }
215867
216623
  if (failures.length > 0) {
215868
216624
  findings.push(...failures);
215869
- } else if (imports.length === 0) {
216625
+ } else if (peerImports.length === 0) {
215870
216626
  findings.push({
215871
216627
  result: makeValidationResult(
215872
216628
  "AP-DEP-005",
@@ -215895,7 +216651,7 @@ async function validateDependencyContractBoundaries(workspaceRoot, architectureC
215895
216651
  ...explicitStandaloneModuleByPath.values()
215896
216652
  ]);
215897
216653
  const importedBySource = buildActualImportedModuleSetBySource(
215898
- imports,
216654
+ peerImports,
215899
216655
  registeredModuleIds
215900
216656
  );
215901
216657
  for (const moduleName of [...registeredModuleIds].sort(
@@ -215910,7 +216666,9 @@ async function validateDependencyContractBoundaries(workspaceRoot, architectureC
215910
216666
  if (sourceContract.status !== "ok" || !sourceContract.contract) {
215911
216667
  continue;
215912
216668
  }
215913
- const declaredDependencies = [...sourceContract.contract.dependsOn].filter((dependencyModule) => registeredModuleIds.has(dependencyModule)).sort((left, right) => left.localeCompare(right));
216669
+ const declaredDependencies = [...sourceContract.contract.dependsOn].filter(
216670
+ (dependencyModule) => registeredModuleIds.has(dependencyModule) && !isParentChildModuleRelationship(moduleName, dependencyModule)
216671
+ ).sort((left, right) => left.localeCompare(right));
215914
216672
  if (declaredDependencies.length === 0) {
215915
216673
  continue;
215916
216674
  }
@@ -215965,13 +216723,16 @@ async function validateDependencyBoundaries(workspaceRoot, options) {
215965
216723
  workspaceRoot,
215966
216724
  parsedArchitectureContract
215967
216725
  );
216726
+ const workspacePackageAliases = await buildWorkspacePackageAliasMap(moduleRoots);
215968
216727
  const imports = await collectCrossModuleImports(
215969
216728
  workspaceRoot,
215970
216729
  modulesRootRelativePath,
215971
216730
  moduleRoots,
215972
216731
  explicitStandaloneModuleByPath,
216732
+ workspacePackageAliases,
215973
216733
  adapter
215974
216734
  );
216735
+ const peerImports = filterPeerModuleImports(imports);
215975
216736
  const findings = [];
215976
216737
  let dependencyRules;
215977
216738
  let dependencyRulesSkipReason;
@@ -215992,7 +216753,7 @@ async function validateDependencyBoundaries(workspaceRoot, options) {
215992
216753
  }
215993
216754
  }
215994
216755
  if (options.checkCircularDependencies) {
215995
- const cycles = detectCircularDependencies([...moduleRoots.keys()], imports);
216756
+ const cycles = detectCircularDependencies([...moduleRoots.keys()], peerImports);
215996
216757
  if (cycles.length === 0) {
215997
216758
  findings.push({
215998
216759
  result: makeValidationResult(
@@ -216024,7 +216785,7 @@ async function validateDependencyBoundaries(workspaceRoot, options) {
216024
216785
  })
216025
216786
  );
216026
216787
  } else {
216027
- const violations = imports.filter((dependencyImport) => {
216788
+ const violations = peerImports.filter((dependencyImport) => {
216028
216789
  const moduleRules = dependencyRules.modules[dependencyImport.sourceModule];
216029
216790
  if (!moduleRules) {
216030
216791
  return false;
@@ -216076,7 +216837,7 @@ async function validateDependencyBoundaries(workspaceRoot, options) {
216076
216837
  })
216077
216838
  );
216078
216839
  } else {
216079
- const violations = imports.filter((dependencyImport) => {
216840
+ const violations = peerImports.filter((dependencyImport) => {
216080
216841
  const moduleRules = dependencyRules.modules[dependencyImport.sourceModule];
216081
216842
  return moduleRules?.publicEntrypointsOnly === true && !dependencyImport.isPublicImport;
216082
216843
  });
@@ -216124,6 +216885,7 @@ async function buildModuleDependencyGraphSnapshot(workspaceRoot, options) {
216124
216885
  workspaceRoot,
216125
216886
  parsedArchitectureContract
216126
216887
  );
216888
+ const workspacePackageAliases = await buildWorkspacePackageAliasMap(moduleRoots);
216127
216889
  const discoveredModules = sortUnique2([
216128
216890
  ...moduleRoots.keys(),
216129
216891
  ...explicitStandaloneModuleByPath.values()
@@ -216136,9 +216898,10 @@ async function buildModuleDependencyGraphSnapshot(workspaceRoot, options) {
216136
216898
  modulesRootRelativePath,
216137
216899
  moduleRoots,
216138
216900
  explicitStandaloneModuleByPath,
216901
+ workspacePackageAliases,
216139
216902
  adapter
216140
216903
  );
216141
- const imports = allImports.filter(
216904
+ const imports = filterPeerModuleImports(allImports).filter(
216142
216905
  (dependencyImport) => moduleSet.has(dependencyImport.sourceModule) && moduleSet.has(dependencyImport.targetModule)
216143
216906
  ).sort((left, right) => {
216144
216907
  const sourceCompare = left.sourceModule.localeCompare(right.sourceModule);
@@ -216191,6 +216954,7 @@ async function buildModuleDependencyGraphSnapshot(workspaceRoot, options) {
216191
216954
  return {
216192
216955
  modules,
216193
216956
  edges,
216957
+ hierarchyEdges: buildModuleHierarchyEdges(modules),
216194
216958
  cycles,
216195
216959
  imports
216196
216960
  };
@@ -219592,11 +220356,11 @@ src/
219592
220356
  },
219593
220357
  DEP_ISOLATED_MODULE_DETECTED: {
219594
220358
  id: ValidationRuleIds.DEP_ISOLATED_MODULE_DETECTED,
219595
- title: "Isolated module detected",
220359
+ title: "Potential orphan module detected",
219596
220360
  category: "dependency",
219597
220361
  defaultSeverity: "warning",
219598
- description: "Checks whether a registered module has no declared or actual inbound/outbound module dependencies.",
219599
- recommendedFix: "Connect the module to the architecture through legitimate dependencies, or remove it if it is unused."
220362
+ description: "Checks whether a registered module has no peer-module dependencies and little evidence of intentional architectural use.",
220363
+ recommendedFix: "Add intentional-use evidence such as a contract, README, public entrypoint, package entrypoint, or remove the module if it is stale."
219600
220364
  },
219601
220365
  DEP_TRANSITIVE_CIRCULAR_MODULE_DEPENDENCY: {
219602
220366
  id: ValidationRuleIds.DEP_TRANSITIVE_CIRCULAR_MODULE_DEPENDENCY,
@@ -220815,8 +221579,17 @@ async function validateArchitectureStyle(workspaceRoot, contract, validationConf
220815
221579
  const modulesRootRelativePath2 = await getConfiguredModulesRoot(workspaceRoot, contract);
220816
221580
  const modulesRoot = path21.join(workspaceRoot, ...modulesRootRelativePath2.split("/"));
220817
221581
  if (await pathExists7(modulesRoot)) {
220818
- const moduleEntries = await import_node_fs20.promises.readdir(modulesRoot, { withFileTypes: true });
220819
- const moduleDirectories2 = moduleEntries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort((left, right) => left.localeCompare(right));
221582
+ const hierarchicalModules = await discoverHierarchicalModuleRoots(
221583
+ workspaceRoot,
221584
+ modulesRootRelativePath2
221585
+ );
221586
+ const moduleDirectories2 = hierarchicalModules.length > 0 ? hierarchicalModules.map((entry) => ({
221587
+ moduleName: entry.moduleId,
221588
+ sourcePath: entry.sourcePath
221589
+ })) : (await import_node_fs20.promises.readdir(modulesRoot, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => ({
221590
+ moduleName: entry.name,
221591
+ sourcePath: `${modulesRootRelativePath2}/${entry.name}`
221592
+ })).sort((left, right) => left.moduleName.localeCompare(right.moduleName));
220820
221593
  const dependencySummary = await buildDependencyGraphSummary(workspaceRoot, contract, {
220821
221594
  modulesRootRelativePath: modulesRootRelativePath2
220822
221595
  });
@@ -220824,8 +221597,9 @@ async function validateArchitectureStyle(workspaceRoot, contract, validationConf
220824
221597
  dependencySummary.modules.filter((moduleSummary) => moduleSummary.inboundDeclaredFrom.length > 0).map((moduleSummary) => moduleSummary.module)
220825
221598
  );
220826
221599
  const incompleteModules = [];
220827
- for (const moduleName of moduleDirectories2) {
220828
- const moduleDir = path21.join(modulesRoot, moduleName);
221600
+ for (const moduleDirectory of moduleDirectories2) {
221601
+ const moduleName = moduleDirectory.moduleName;
221602
+ const moduleDir = path21.join(workspaceRoot, ...moduleDirectory.sourcePath.split("/"));
220829
221603
  const readmeExists = await pathExists7(path21.join(moduleDir, "README.md"));
220830
221604
  const indexExists = await pathExists7(path21.join(moduleDir, "index.ts"));
220831
221605
  const registryPublicEntrypoints = (contract.modules[moduleName]?.publicEntrypoints ?? []).filter((entry) => typeof entry === "string").map((entry) => normalizeEntrypoint(entry));
@@ -220954,18 +221728,111 @@ async function validateDependencyGraphIsolation(workspaceRoot, contract, depende
220954
221728
  return [];
220955
221729
  }
220956
221730
  const isolatedResults = [];
220957
- const isolationExemptInfrastructureModules = /* @__PURE__ */ new Set(["ci", "policy", "validation"]);
221731
+ const sourceFileExtensions4 = /* @__PURE__ */ new Set([
221732
+ ".ts",
221733
+ ".tsx",
221734
+ ".js",
221735
+ ".jsx",
221736
+ ".mjs",
221737
+ ".cjs",
221738
+ ".mts",
221739
+ ".cts",
221740
+ ".py",
221741
+ ".go",
221742
+ ".java",
221743
+ ".kt",
221744
+ ".kts",
221745
+ ".cs",
221746
+ ".php",
221747
+ ".rb",
221748
+ ".vue",
221749
+ ".svelte"
221750
+ ]);
220958
221751
  const isTestLikeModule = (moduleName) => {
220959
221752
  const normalized = moduleName.toLowerCase();
220960
221753
  return normalized === "test" || normalized === "tests" || normalized.endsWith("-test") || normalized.endsWith("-tests");
220961
221754
  };
221755
+ const collectModuleSourceEvidence2 = async (directoryPath) => {
221756
+ let entries;
221757
+ try {
221758
+ entries = await import_node_fs20.promises.readdir(directoryPath, { withFileTypes: true });
221759
+ } catch {
221760
+ return { sourceFileCount: 0, hasReadme: false, hasEntrypointFile: false };
221761
+ }
221762
+ let sourceFileCount = 0;
221763
+ let hasReadme = false;
221764
+ let hasEntrypointFile = false;
221765
+ for (const entry of entries) {
221766
+ const fullPath = path21.join(directoryPath, entry.name);
221767
+ if (entry.isDirectory()) {
221768
+ if (["node_modules", "dist", "out", "build", "coverage", ".git"].includes(entry.name)) {
221769
+ continue;
221770
+ }
221771
+ const nested = await collectModuleSourceEvidence2(fullPath);
221772
+ sourceFileCount += nested.sourceFileCount;
221773
+ hasReadme ||= nested.hasReadme;
221774
+ hasEntrypointFile ||= nested.hasEntrypointFile;
221775
+ continue;
221776
+ }
221777
+ if (!entry.isFile()) {
221778
+ continue;
221779
+ }
221780
+ const lowerName = entry.name.toLowerCase();
221781
+ if (lowerName === "readme.md" || lowerName === "readme.mdx") {
221782
+ hasReadme = true;
221783
+ }
221784
+ if (/^(index|main|app|extension|cli)\.[cm]?[jt]sx?$/iu.test(entry.name)) {
221785
+ hasEntrypointFile = true;
221786
+ }
221787
+ if (sourceFileExtensions4.has(path21.extname(entry.name).toLowerCase())) {
221788
+ sourceFileCount += 1;
221789
+ }
221790
+ }
221791
+ return { sourceFileCount, hasReadme, hasEntrypointFile };
221792
+ };
221793
+ const readPackageJsonIfExists2 = async (moduleDirectoryPath) => {
221794
+ const content = await readTextFileIfExists3(path21.join(moduleDirectoryPath, "package.json"));
221795
+ if (!content) {
221796
+ return void 0;
221797
+ }
221798
+ try {
221799
+ const parsed = JSON.parse(content);
221800
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
221801
+ } catch {
221802
+ return void 0;
221803
+ }
221804
+ };
221805
+ const hasPackageEntrypoint2 = async (moduleDirectoryPath) => {
221806
+ const packageJson = await readPackageJsonIfExists2(moduleDirectoryPath);
221807
+ if (!packageJson) {
221808
+ return false;
221809
+ }
221810
+ return ["main", "module", "browser", "types", "bin", "exports"].some(
221811
+ (field) => packageJson[field] !== void 0
221812
+ );
221813
+ };
221814
+ const hasKnownBoundaryRole2 = (moduleName) => {
221815
+ const normalized = moduleName.toLowerCase();
221816
+ const compact = normalized.replace(/[-_/]/gu, "");
221817
+ const segments = normalized.split(/[/-]/u);
221818
+ return ["adr", "ci", "compliance", "config", "governance", "policy", "validation", "smartinit"].some(
221819
+ (role) => compact.includes(role) || segments.includes(role)
221820
+ );
221821
+ };
221822
+ const hasConfiguredPublicEntrypoint2 = (moduleName) => {
221823
+ const publicEntrypoints = contract.modules[moduleName]?.publicEntrypoints;
221824
+ return Array.isArray(publicEntrypoints) && publicEntrypoints.some(
221825
+ (entry) => typeof entry === "string" && entry.trim().length > 0
221826
+ );
221827
+ };
221828
+ const hasIntentionalIndependentEvidence2 = async (moduleSummary, moduleDirectoryPath) => {
221829
+ const sourceEvidence = await collectModuleSourceEvidence2(moduleDirectoryPath);
221830
+ return moduleSummary.hasContract || hasConfiguredPublicEntrypoint2(moduleSummary.module) || sourceEvidence.hasReadme || sourceEvidence.hasEntrypointFile || await hasPackageEntrypoint2(moduleDirectoryPath) || moduleSummary.childModules.length > 0 || moduleSummary.parentModule !== void 0 && moduleSummary.childModules.length === 0 && sourceEvidence.sourceFileCount > 0 || hasKnownBoundaryRole2(moduleSummary.module) || sourceEvidence.sourceFileCount >= 2;
221831
+ };
220962
221832
  for (const moduleSummary of dependencyGraphSummary.modules) {
220963
221833
  if (isTestLikeModule(moduleSummary.module)) {
220964
221834
  continue;
220965
221835
  }
220966
- if (isolationExemptInfrastructureModules.has(moduleSummary.module.toLowerCase())) {
220967
- continue;
220968
- }
220969
221836
  const registryEntry = contract.modules[moduleSummary.module];
220970
221837
  if (!registryEntry?.path) {
220971
221838
  continue;
@@ -220979,11 +221846,14 @@ async function validateDependencyGraphIsolation(workspaceRoot, contract, depende
220979
221846
  if (!isIsolated) {
220980
221847
  continue;
220981
221848
  }
221849
+ if (await hasIntentionalIndependentEvidence2(moduleSummary, moduleDirectoryPath)) {
221850
+ continue;
221851
+ }
220982
221852
  isolatedResults.push({
220983
221853
  id: ValidationRuleIds.DEP_ISOLATED_MODULE_DETECTED,
220984
221854
  severity: "warning",
220985
221855
  passed: false,
220986
- message: `Module '${moduleSummary.module}' is isolated: it has no declared or actual inbound/outbound module dependencies.`,
221856
+ message: `Module '${moduleSummary.module}' is a potential orphan: it has no declared or actual inbound/outbound module dependencies and little evidence of intentional architectural use.`,
220987
221857
  findingType: "module-dependency",
220988
221858
  module: moduleSummary.module
220989
221859
  });
@@ -220996,7 +221866,7 @@ async function validateDependencyGraphIsolation(workspaceRoot, contract, depende
220996
221866
  id: ValidationRuleIds.DEP_ISOLATED_MODULE_DETECTED,
220997
221867
  severity: "warning",
220998
221868
  passed: true,
220999
- message: "No isolated modules detected among registered modules with existing registry paths."
221869
+ message: "No potential orphan modules detected among registered modules with existing registry paths."
221000
221870
  }
221001
221871
  ];
221002
221872
  }
@@ -221778,7 +222648,10 @@ function filterDependencyGraphSummaryByModuleScope(dependencyGraphSummary, modul
221778
222648
  )
221779
222649
  }));
221780
222650
  return {
221781
- modules: scopedModules
222651
+ modules: scopedModules,
222652
+ hierarchyEdges: dependencyGraphSummary.hierarchyEdges.filter(
222653
+ (edge) => scopeSet.has(edge.parentModule) && scopeSet.has(edge.childModule)
222654
+ )
221782
222655
  };
221783
222656
  }
221784
222657
  async function runArchitectureValidationForWorkspace(workspaceRoot, options) {
@@ -224779,9 +225652,32 @@ async function readTextFileIfExists4(filePath) {
224779
225652
  return void 0;
224780
225653
  }
224781
225654
  }
225655
+ var sourceFileExtensions2 = /* @__PURE__ */ new Set([
225656
+ ".ts",
225657
+ ".tsx",
225658
+ ".js",
225659
+ ".jsx",
225660
+ ".mjs",
225661
+ ".cjs",
225662
+ ".mts",
225663
+ ".cts",
225664
+ ".py",
225665
+ ".go",
225666
+ ".java",
225667
+ ".kt",
225668
+ ".kts",
225669
+ ".cs",
225670
+ ".php",
225671
+ ".rb",
225672
+ ".vue",
225673
+ ".svelte"
225674
+ ]);
224782
225675
  function sortUnique3(values) {
224783
225676
  return [...new Set(values)].sort((left, right) => left.localeCompare(right));
224784
225677
  }
225678
+ function toSafeContractModuleFileStem2(moduleName) {
225679
+ return normalizePath5(moduleName).split("/").map((segment) => segment.trim()).filter((segment) => segment.length > 0).join(".");
225680
+ }
224785
225681
  function getModuleRegistry(contract) {
224786
225682
  const modules = contract?.modules;
224787
225683
  if (!modules || typeof modules !== "object" || Array.isArray(modules)) {
@@ -224792,6 +225688,83 @@ function getModuleRegistry(contract) {
224792
225688
  function normalizePublicEntrypointList(entries) {
224793
225689
  return sortUnique3(entries.map((entry) => normalizePath5(entry.trim())).filter((entry) => entry.length > 0));
224794
225690
  }
225691
+ function hasConfiguredPublicEntrypoint(registryEntry) {
225692
+ return Array.isArray(registryEntry?.publicEntrypoints) && registryEntry.publicEntrypoints.some(
225693
+ (entry) => typeof entry === "string" && entry.trim().length > 0
225694
+ );
225695
+ }
225696
+ async function readPackageJsonIfExists(moduleDirectoryPath) {
225697
+ const content = await readTextFileIfExists4(path26.join(moduleDirectoryPath, "package.json"));
225698
+ if (!content) {
225699
+ return void 0;
225700
+ }
225701
+ try {
225702
+ const parsed = JSON.parse(content);
225703
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
225704
+ } catch {
225705
+ return void 0;
225706
+ }
225707
+ }
225708
+ async function collectModuleSourceEvidence(directoryPath) {
225709
+ let entries;
225710
+ try {
225711
+ entries = await import_node_fs24.promises.readdir(directoryPath, { withFileTypes: true });
225712
+ } catch {
225713
+ return { sourceFileCount: 0, hasReadme: false, hasEntrypointFile: false };
225714
+ }
225715
+ let sourceFileCount = 0;
225716
+ let hasReadme = false;
225717
+ let hasEntrypointFile = false;
225718
+ for (const entry of entries) {
225719
+ const fullPath = path26.join(directoryPath, entry.name);
225720
+ if (entry.isDirectory()) {
225721
+ if (["node_modules", "dist", "out", "build", "coverage", ".git"].includes(entry.name)) {
225722
+ continue;
225723
+ }
225724
+ const nested = await collectModuleSourceEvidence(fullPath);
225725
+ sourceFileCount += nested.sourceFileCount;
225726
+ hasReadme ||= nested.hasReadme;
225727
+ hasEntrypointFile ||= nested.hasEntrypointFile;
225728
+ continue;
225729
+ }
225730
+ if (!entry.isFile()) {
225731
+ continue;
225732
+ }
225733
+ const lowerName = entry.name.toLowerCase();
225734
+ if (lowerName === "readme.md" || lowerName === "readme.mdx") {
225735
+ hasReadme = true;
225736
+ }
225737
+ if (/^(index|main|app|extension|cli)\.[cm]?[jt]sx?$/iu.test(entry.name)) {
225738
+ hasEntrypointFile = true;
225739
+ }
225740
+ if (sourceFileExtensions2.has(path26.extname(entry.name).toLowerCase())) {
225741
+ sourceFileCount += 1;
225742
+ }
225743
+ }
225744
+ return { sourceFileCount, hasReadme, hasEntrypointFile };
225745
+ }
225746
+ function hasKnownBoundaryRole(moduleName) {
225747
+ const normalized = moduleName.toLowerCase();
225748
+ const compact = normalized.replace(/[-_/]/gu, "");
225749
+ const segments = normalized.split(/[/-]/u);
225750
+ return ["adr", "ci", "compliance", "config", "governance", "policy", "validation", "smartinit"].some(
225751
+ (role) => compact.includes(role) || segments.includes(role)
225752
+ );
225753
+ }
225754
+ async function hasPackageEntrypoint(moduleDirectoryPath) {
225755
+ const packageJson = await readPackageJsonIfExists(moduleDirectoryPath);
225756
+ if (!packageJson) {
225757
+ return false;
225758
+ }
225759
+ return ["main", "module", "browser", "types", "bin", "exports"].some(
225760
+ (field) => packageJson[field] !== void 0
225761
+ );
225762
+ }
225763
+ async function hasIntentionalIndependentEvidence(args) {
225764
+ const moduleDirectoryPath = path26.join(args.workspaceRoot, ...args.moduleEntry.sourcePath.split("/"));
225765
+ const evidence = await collectModuleSourceEvidence(moduleDirectoryPath);
225766
+ return args.moduleEntry.contractExists || args.moduleEntry.hasPublicEntrypoint === true || evidence.hasReadme || evidence.hasEntrypointFile || await hasPackageEntrypoint(moduleDirectoryPath) || args.hasChildren || args.isLeafChild && evidence.sourceFileCount > 0 || hasKnownBoundaryRole(args.moduleEntry.moduleName) || evidence.sourceFileCount >= 2;
225767
+ }
224795
225768
  async function loadModuleContractPublicEntrypoints(workspaceRoot, contractPath) {
224796
225769
  const fullContractPath = path26.join(workspaceRoot, ...contractPath.split("/"));
224797
225770
  const content = await readTextFileIfExists4(fullContractPath);
@@ -224832,7 +225805,7 @@ async function discoverModulesFromArchitectureContract(workspaceRoot, contract,
224832
225805
  const discovered = [];
224833
225806
  const discoveredNames = /* @__PURE__ */ new Set();
224834
225807
  const toContractPath = (moduleName, registryEntry) => normalizePath5(
224835
- typeof registryEntry?.contract === "string" ? registryEntry.contract : `${contractsRoot}/${moduleName}.contract.json`
225808
+ typeof registryEntry?.contract === "string" ? registryEntry.contract : `${contractsRoot}/${toSafeContractModuleFileStem2(moduleName)}.contract.json`
224836
225809
  );
224837
225810
  const resolveMissingPublicEntrypointAlignment = async (registryEntry, contractPath) => {
224838
225811
  if (!Array.isArray(registryEntry?.publicEntrypoints)) {
@@ -224876,7 +225849,7 @@ async function discoverModulesFromArchitectureContract(workspaceRoot, contract,
224876
225849
  continue;
224877
225850
  }
224878
225851
  const contractPath = normalizePath5(
224879
- typeof registryEntry.contract === "string" ? registryEntry.contract : `${contractsRoot}/${moduleName}.contract.json`
225852
+ typeof registryEntry.contract === "string" ? registryEntry.contract : `${contractsRoot}/${toSafeContractModuleFileStem2(moduleName)}.contract.json`
224880
225853
  );
224881
225854
  const contractExists = await pathExists10(path26.join(workspaceRoot, ...contractPath.split("/")));
224882
225855
  const missingPublicEntrypointAlignment = await resolveMissingPublicEntrypointAlignment(
@@ -224891,6 +225864,7 @@ async function discoverModulesFromArchitectureContract(workspaceRoot, contract,
224891
225864
  ...resolveModuleScopeForPath(workspaceRoot, normalizedSourcePath, contract) ? { scope: resolveModuleScopeForPath(workspaceRoot, normalizedSourcePath, contract) } : {},
224892
225865
  contractPath,
224893
225866
  contractExists,
225867
+ hasPublicEntrypoint: hasConfiguredPublicEntrypoint(registryEntry),
224894
225868
  ...missingPublicEntrypointAlignment !== void 0 ? { missingPublicEntrypointAlignment } : {}
224895
225869
  });
224896
225870
  discoveredNames.add(moduleName);
@@ -224915,18 +225889,56 @@ async function discoverModulesFromArchitectureContract(workspaceRoot, contract,
224915
225889
  ...resolveModuleScopeForPath(workspaceRoot, sourcePath, contract) ? { scope: resolveModuleScopeForPath(workspaceRoot, sourcePath, contract) } : {},
224916
225890
  contractPath,
224917
225891
  contractExists,
225892
+ hasPublicEntrypoint: hasConfiguredPublicEntrypoint(registryEntry),
224918
225893
  ...missingPublicEntrypointAlignment !== void 0 ? { missingPublicEntrypointAlignment } : {}
224919
225894
  });
224920
225895
  discoveredNames.add(moduleName);
224921
225896
  }
225897
+ for (const moduleRoot of await discoverHierarchicalModuleRoots(workspaceRoot, normalizedModulesRoot)) {
225898
+ if (discoveredNames.has(moduleRoot.moduleId)) {
225899
+ continue;
225900
+ }
225901
+ const registryEntry = moduleRegistry[moduleRoot.moduleId];
225902
+ const contractPath = toContractPath(moduleRoot.moduleId, registryEntry);
225903
+ const contractExists = await pathExists10(path26.join(workspaceRoot, ...contractPath.split("/")));
225904
+ const missingPublicEntrypointAlignment = await resolveMissingPublicEntrypointAlignment(
225905
+ registryEntry,
225906
+ contractPath
225907
+ );
225908
+ discovered.push({
225909
+ moduleName: moduleRoot.moduleId,
225910
+ sourcePath: moduleRoot.sourcePath,
225911
+ discoverySource: "architecture.json",
225912
+ sourcePathExists: true,
225913
+ ...resolveModuleScopeForPath(workspaceRoot, moduleRoot.sourcePath, contract) ? { scope: resolveModuleScopeForPath(workspaceRoot, moduleRoot.sourcePath, contract) } : {},
225914
+ contractPath,
225915
+ contractExists,
225916
+ hasPublicEntrypoint: hasConfiguredPublicEntrypoint(registryEntry),
225917
+ ...missingPublicEntrypointAlignment !== void 0 ? { missingPublicEntrypointAlignment } : {}
225918
+ });
225919
+ discoveredNames.add(moduleRoot.moduleId);
225920
+ }
224922
225921
  return discovered.sort((left, right) => left.moduleName.localeCompare(right.moduleName));
224923
225922
  }
224924
225923
  async function discoverModulesFromFallbackScan(workspaceRoot, modulesRoot = "src/modules", contractsRoot = ".archpilot/contracts") {
224925
- const moduleNames = await listModuleDirectoryNames(workspaceRoot, modulesRoot);
225924
+ const hierarchicalModules = await discoverHierarchicalModuleRoots(workspaceRoot, modulesRoot);
225925
+ const moduleNames = hierarchicalModules.length > 0 ? [] : await listModuleDirectoryNames(workspaceRoot, modulesRoot);
224926
225926
  const discovered = [];
225927
+ for (const moduleRoot of hierarchicalModules) {
225928
+ const contractPath = `${contractsRoot}/${toSafeContractModuleFileStem2(moduleRoot.moduleId)}.contract.json`;
225929
+ discovered.push({
225930
+ moduleName: moduleRoot.moduleId,
225931
+ sourcePath: moduleRoot.sourcePath,
225932
+ discoverySource: "inferred-scan",
225933
+ sourcePathExists: true,
225934
+ ...resolveModuleScopeForPath(workspaceRoot, moduleRoot.sourcePath) ? { scope: resolveModuleScopeForPath(workspaceRoot, moduleRoot.sourcePath) } : {},
225935
+ contractPath,
225936
+ contractExists: await pathExists10(path26.join(workspaceRoot, ...contractPath.split("/")))
225937
+ });
225938
+ }
224927
225939
  for (const moduleName of moduleNames) {
224928
225940
  const sourcePath = `${modulesRoot}/${moduleName}`;
224929
- const contractPath = `${contractsRoot}/${moduleName}.contract.json`;
225941
+ const contractPath = `${contractsRoot}/${toSafeContractModuleFileStem2(moduleName)}.contract.json`;
224930
225942
  discovered.push({
224931
225943
  moduleName,
224932
225944
  sourcePath,
@@ -225024,14 +226036,39 @@ async function generateArchitectureMap(workspaceRoot, options) {
225024
226036
  moduleFilter: moduleIds
225025
226037
  });
225026
226038
  const edges = dependencySnapshot.edges.map((edge) => toArchitectureMapEdge(edge));
226039
+ const hierarchyEdges = dependencySnapshot.hierarchyEdges;
225027
226040
  const parsedCycles = dependencySnapshot.cycles.map((cycle) => parseCycleString(cycle)).filter((cycle) => cycle.length > 1).sort((left, right) => left.join("->").localeCompare(right.join("->")));
225028
226041
  const incomingMap = buildIncomingMap(modules, edges);
225029
226042
  const outgoingMap = buildOutgoingMap(modules, edges);
225030
226043
  const cycleMembership = new Set(parsedCycles.flatMap((cycle) => cycle));
225031
- const hotspotSummary = modules.map((moduleEntry) => {
226044
+ const hierarchyParentByChild = new Map(
226045
+ hierarchyEdges.map((edge) => [edge.childModule, edge.parentModule])
226046
+ );
226047
+ const childModulesByParent = /* @__PURE__ */ new Map();
226048
+ for (const edge of hierarchyEdges) {
226049
+ const children = childModulesByParent.get(edge.parentModule) ?? [];
226050
+ children.push(edge.childModule);
226051
+ childModulesByParent.set(edge.parentModule, children);
226052
+ }
226053
+ const independentModuleNames = [];
226054
+ const potentialOrphanModuleNames = [];
226055
+ const hotspotSummary = await Promise.all(modules.map(async (moduleEntry) => {
225032
226056
  const incomingDependencyCount = incomingMap.get(moduleEntry.moduleName)?.length ?? 0;
225033
226057
  const outgoingDependencyCount = outgoingMap.get(moduleEntry.moduleName)?.length ?? 0;
225034
- const orphan = incomingDependencyCount === 0 && outgoingDependencyCount === 0;
226058
+ const hasNoPeerUsage = incomingDependencyCount === 0 && outgoingDependencyCount === 0;
226059
+ const hasIntentionalEvidence = hasNoPeerUsage ? await hasIntentionalIndependentEvidence({
226060
+ workspaceRoot,
226061
+ moduleEntry,
226062
+ hasChildren: (childModulesByParent.get(moduleEntry.moduleName)?.length ?? 0) > 0,
226063
+ isLeafChild: hierarchyParentByChild.has(moduleEntry.moduleName) && (childModulesByParent.get(moduleEntry.moduleName)?.length ?? 0) === 0
226064
+ }) : false;
226065
+ const orphan = hasNoPeerUsage && !hasIntentionalEvidence;
226066
+ if (hasNoPeerUsage && hasIntentionalEvidence) {
226067
+ independentModuleNames.push(moduleEntry.moduleName);
226068
+ }
226069
+ if (orphan) {
226070
+ potentialOrphanModuleNames.push(moduleEntry.moduleName);
226071
+ }
225035
226072
  return {
225036
226073
  moduleName: moduleEntry.moduleName,
225037
226074
  incomingDependencyCount,
@@ -225043,7 +226080,7 @@ async function generateArchitectureMap(workspaceRoot, options) {
225043
226080
  missingPublicEntrypointAlignment: moduleEntry.missingPublicEntrypointAlignment
225044
226081
  } : {}
225045
226082
  };
225046
- });
226083
+ }));
225047
226084
  const mostDependedOnModules = sortRankedCounts(
225048
226085
  hotspotSummary.filter((entry) => entry.incomingDependencyCount > 0).map((entry) => ({
225049
226086
  moduleName: entry.moduleName,
@@ -225059,9 +226096,9 @@ async function generateArchitectureMap(workspaceRoot, options) {
225059
226096
  const modulesInCircularDependencies = sortUnique3(
225060
226097
  hotspotSummary.filter((entry) => entry.participatesInCycle).map((entry) => entry.moduleName)
225061
226098
  );
225062
- const orphanModules = sortUnique3(
225063
- hotspotSummary.filter((entry) => entry.orphan).map((entry) => entry.moduleName)
225064
- );
226099
+ const independentModules = sortUnique3(independentModuleNames);
226100
+ const potentialOrphanModules = sortUnique3(potentialOrphanModuleNames);
226101
+ const orphanModules = potentialOrphanModules;
225065
226102
  const missingContracts = sortUnique3(
225066
226103
  hotspotSummary.filter((entry) => entry.missingContract).map((entry) => entry.moduleName)
225067
226104
  );
@@ -225084,11 +226121,14 @@ async function generateArchitectureMap(workspaceRoot, options) {
225084
226121
  modules,
225085
226122
  moduleHotspotSummary: hotspotSummary,
225086
226123
  edges,
226124
+ hierarchyEdges,
225087
226125
  cycles: parsedCycles,
225088
226126
  hotspots: {
225089
226127
  mostDependedOnModules,
225090
226128
  highestOutgoingFanOutModules,
225091
226129
  modulesInCircularDependencies,
226130
+ independentModules,
226131
+ potentialOrphanModules,
225092
226132
  orphanModules,
225093
226133
  missingContracts,
225094
226134
  highlyConnectedModules,
@@ -225098,6 +226138,8 @@ async function generateArchitectureMap(workspaceRoot, options) {
225098
226138
  moduleCount: modules.length,
225099
226139
  edgeCount: edges.length,
225100
226140
  cycleCount: parsedCycles.length,
226141
+ independentCount: independentModules.length,
226142
+ potentialOrphanCount: potentialOrphanModules.length,
225101
226143
  orphanCount: orphanModules.length,
225102
226144
  highlyConnectedCount: highlyConnectedModules.length,
225103
226145
  missingContractCount: missingContracts.length
@@ -225113,6 +226155,11 @@ async function generateArchitectureMap(workspaceRoot, options) {
225113
226155
  importCount: edge.importCount,
225114
226156
  publicImportCount: edge.publicImportCount,
225115
226157
  nonPublicImportCount: edge.nonPublicImportCount
226158
+ })),
226159
+ hierarchyEdges: hierarchyEdges.map((edge) => ({
226160
+ source: edge.parentModule,
226161
+ target: edge.childModule,
226162
+ relationship: "contains"
225116
226163
  }))
225117
226164
  },
225118
226165
  notes: {
@@ -230143,6 +231190,9 @@ async function pathExists13(targetPath) {
230143
231190
  function sortUnique12(values) {
230144
231191
  return [...new Set(values)].sort((left, right) => left.localeCompare(right));
230145
231192
  }
231193
+ function toSafeContractModuleFileStem3(moduleName) {
231194
+ return normalizePath11(moduleName).split("/").map((segment) => segment.trim()).filter((segment) => segment.length > 0).join(".");
231195
+ }
230146
231196
  function wildcardToRegex(pattern) {
230147
231197
  const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
230148
231198
  return new RegExp(`^${escaped.replace(/\*/g, ".*")}$`, "u");
@@ -230224,7 +231274,10 @@ function resolveContractPath(contract, moduleName) {
230224
231274
  return normalizePath11(configured);
230225
231275
  }
230226
231276
  return normalizePath11(
230227
- path44.posix.join(resolveContractsRootFromContract(contract), `${moduleName}.contract.json`)
231277
+ path44.posix.join(
231278
+ resolveContractsRootFromContract(contract),
231279
+ `${toSafeContractModuleFileStem3(moduleName)}.contract.json`
231280
+ )
230228
231281
  );
230229
231282
  }
230230
231283
  function createModuleContract(input2) {
@@ -230236,6 +231289,23 @@ function createModuleContract(input2) {
230236
231289
  exposedApis: []
230237
231290
  };
230238
231291
  }
231292
+ async function readExistingModuleContractPublicEntrypoints(workspaceRoot, contractPath) {
231293
+ try {
231294
+ const raw = await import_node_fs39.promises.readFile(toAbsolutePath(workspaceRoot, contractPath), {
231295
+ encoding: "utf8"
231296
+ });
231297
+ const parsed = JSON.parse(raw);
231298
+ if (!Array.isArray(parsed.publicEntrypoints)) {
231299
+ return void 0;
231300
+ }
231301
+ if (parsed.publicEntrypoints.some((entry) => typeof entry !== "string")) {
231302
+ return void 0;
231303
+ }
231304
+ return sortUnique12(parsed.publicEntrypoints.map((entry) => normalizePath11(entry)));
231305
+ } catch {
231306
+ return void 0;
231307
+ }
231308
+ }
230239
231309
  function isRegistryEntryDifferent(input2) {
230240
231310
  const moduleRegistry = getModuleRegistry2(input2.contract);
230241
231311
  const existing = moduleRegistry[input2.moduleName] ?? {};
@@ -230262,7 +231332,15 @@ async function generateMissingModuleContracts(workspaceRoot) {
230262
231332
  )) {
230263
231333
  const contractPath = resolveContractPath(contract, moduleEntry.moduleName);
230264
231334
  const sourcePath = normalizePath11(moduleEntry.sourcePath);
230265
- const publicEntrypoints = await inferModulePublicEntrypoints(
231335
+ const absoluteContractPath = toAbsolutePath(workspaceRoot, contractPath);
231336
+ const contractExists = await pathExists13(absoluteContractPath);
231337
+ const existingContractPublicEntrypoints = contractExists ? await readExistingModuleContractPublicEntrypoints(workspaceRoot, contractPath) : void 0;
231338
+ const configuredPublicEntrypoints = !contractExists && Array.isArray(getModuleRegistry2(contract)[moduleEntry.moduleName]?.publicEntrypoints) ? sortUnique12(
231339
+ getModuleRegistry2(contract)[moduleEntry.moduleName]?.publicEntrypoints?.map(
231340
+ (entry) => normalizePath11(entry)
231341
+ ) ?? []
231342
+ ) : void 0;
231343
+ const publicEntrypoints = existingContractPublicEntrypoints ?? configuredPublicEntrypoints ?? await inferModulePublicEntrypoints(
230266
231344
  workspaceRoot,
230267
231345
  sourcePath,
230268
231346
  adapter
@@ -230272,8 +231350,7 @@ async function generateMissingModuleContracts(workspaceRoot) {
230272
231350
  dependsOn: dependsOnBySource.get(moduleEntry.moduleName) ?? [],
230273
231351
  publicEntrypoints
230274
231352
  });
230275
- const absoluteContractPath = toAbsolutePath(workspaceRoot, contractPath);
230276
- if (await pathExists13(absoluteContractPath)) {
231353
+ if (contractExists) {
230277
231354
  skippedExistingContracts.push(contractPath);
230278
231355
  } else {
230279
231356
  await import_node_fs39.promises.mkdir(path44.dirname(absoluteContractPath), { recursive: true });
@@ -231635,6 +232712,7 @@ var ignoredDirectoryNames2 = /* @__PURE__ */ new Set([
231635
232712
  ]);
231636
232713
  var backendStackIds = /* @__PURE__ */ new Set([
231637
232714
  "spring",
232715
+ "express",
231638
232716
  "fastapi",
231639
232717
  "django",
231640
232718
  "flask",
@@ -231643,7 +232721,11 @@ var backendStackIds = /* @__PURE__ */ new Set([
231643
232721
  "java",
231644
232722
  "python",
231645
232723
  "php",
232724
+ "laravel",
231646
232725
  "go",
232726
+ "ruby_rails",
232727
+ "kotlin",
232728
+ "kotlin_spring",
231647
232729
  "node_typescript",
231648
232730
  "node_javascript"
231649
232731
  ]);
@@ -231861,6 +232943,7 @@ function buildDetectionContext(workspaceRoot, filePaths, pathsByFileName, extens
231861
232943
  function toDetectedStack(adapterId) {
231862
232944
  switch (adapterId) {
231863
232945
  case "node_typescript":
232946
+ case "express":
231864
232947
  case "nestjs":
231865
232948
  case "java":
231866
232949
  case "spring":
@@ -231870,7 +232953,11 @@ function toDetectedStack(adapterId) {
231870
232953
  case "dotnet":
231871
232954
  case "python":
231872
232955
  case "php":
232956
+ case "laravel":
231873
232957
  case "go":
232958
+ case "ruby_rails":
232959
+ case "kotlin":
232960
+ case "kotlin_spring":
231874
232961
  case "react":
231875
232962
  case "nextjs":
231876
232963
  case "angular":
@@ -231891,6 +232978,9 @@ function resolveLanguages(stacks) {
231891
232978
  if (stacks.includes("spring")) {
231892
232979
  languages.add("java");
231893
232980
  }
232981
+ if (stacks.includes("kotlin") || stacks.includes("kotlin_spring")) {
232982
+ languages.add("kotlin");
232983
+ }
231894
232984
  if (stacks.includes("dotnet")) {
231895
232985
  languages.add("csharp");
231896
232986
  }
@@ -231909,6 +232999,12 @@ function resolveLanguages(stacks) {
231909
232999
  if (stacks.includes("php")) {
231910
233000
  languages.add("php");
231911
233001
  }
233002
+ if (stacks.includes("laravel")) {
233003
+ languages.add("php");
233004
+ }
233005
+ if (stacks.includes("ruby_rails")) {
233006
+ languages.add("ruby");
233007
+ }
231912
233008
  if (stacks.includes("go")) {
231913
233009
  languages.add("go");
231914
233010
  }
@@ -231922,6 +233018,12 @@ function resolveLanguages(stacks) {
231922
233018
  languages.add("typescript");
231923
233019
  languages.add("javascript");
231924
233020
  }
233021
+ if (stacks.includes("express")) {
233022
+ languages.add("javascript");
233023
+ if (stacks.includes("node_typescript")) {
233024
+ languages.add("typescript");
233025
+ }
233026
+ }
231925
233027
  if (stacks.includes("react") || stacks.includes("nextjs") || stacks.includes("angular") || stacks.includes("vue")) {
231926
233028
  languages.add(stacks.includes("node_typescript") ? "typescript" : "javascript");
231927
233029
  }
@@ -232394,11 +233496,12 @@ var appRouteReservedDirectoryNames = /* @__PURE__ */ new Set([
232394
233496
  "common",
232395
233497
  "shared-ui"
232396
233498
  ]);
232397
- var sourceFileExtensions = /* @__PURE__ */ new Set([
233499
+ var sourceFileExtensions3 = /* @__PURE__ */ new Set([
232398
233500
  ".ts",
232399
233501
  ".tsx",
232400
233502
  ".js",
232401
233503
  ".jsx",
233504
+ ".vue",
232402
233505
  ".java",
232403
233506
  ".py",
232404
233507
  ".php",
@@ -232433,7 +233536,9 @@ var roleFileHints = [
232433
233536
  "layout.tsx",
232434
233537
  "layout.jsx",
232435
233538
  "route.ts",
232436
- "route.js"
233539
+ "route.js",
233540
+ "view.vue",
233541
+ "page.vue"
232437
233542
  ];
232438
233543
  function normalizePath13(value) {
232439
233544
  return value.replaceAll("\\", "/");
@@ -232509,7 +233614,7 @@ function inspectModuleFolder(absolutePath) {
232509
233614
  continue;
232510
233615
  }
232511
233616
  const extension = path53.extname(entry.name).toLowerCase();
232512
- if (sourceFileExtensions.has(extension)) {
233617
+ if (sourceFileExtensions3.has(extension)) {
232513
233618
  hasSourceFiles = true;
232514
233619
  sourceFileCount += 1;
232515
233620
  }
@@ -232589,14 +233694,17 @@ function collectModuleCandidatesFromRoot(workspaceRoot, modulesRootRelative, def
232589
233694
  if (skipFrontendUtilities && frontendUtilityDirectoryNames.has(entry.name.toLowerCase())) {
232590
233695
  continue;
232591
233696
  }
232592
- const moduleName = entry.name;
232593
- const moduleRelativePath = normalizePath13(`${modulesRootRelative}/${moduleName}`);
233697
+ const rootSegments = modulesRootRelative.split("/").filter((segment) => segment.length > 0);
233698
+ const sourceIndex = rootSegments.lastIndexOf("src");
233699
+ const rootLeaf = rootSegments[rootSegments.length - 1]?.toLowerCase();
233700
+ const parentName = sourceIndex > 0 && (rootLeaf === "src" || ["modules", "features"].includes(rootLeaf ?? "")) ? rootSegments[sourceIndex - 1] : void 0;
233701
+ const moduleName = parentName && parentName !== "src" ? `${parentName}/${entry.name}` : ["domain", "domains", "application", "infrastructure"].includes(rootLeaf ?? "") ? `${rootSegments[rootSegments.length - 1]}/${entry.name}` : entry.name;
232594
233702
  const moduleAbsolutePath = path53.join(absoluteRoot, entry.name);
232595
233703
  const inspected = inspectModuleFolder(moduleAbsolutePath);
232596
233704
  const detected = makeDetectedModule(
232597
233705
  workspaceRoot,
232598
233706
  moduleName,
232599
- moduleRelativePath,
233707
+ normalizePath13(`${modulesRootRelative}/${entry.name}`),
232600
233708
  modulesRootRelative,
232601
233709
  inspected.hasSourceFiles,
232602
233710
  inspected.hasRoleFiles,
@@ -232657,12 +233765,22 @@ function collectExplicitRoots(workspaceRoot) {
232657
233765
  for (const root of [
232658
233766
  "src/modules",
232659
233767
  "src/features",
233768
+ "src/domain",
233769
+ "src/domains",
233770
+ "src/application",
233771
+ "src/infrastructure",
232660
233772
  "src/app",
232661
233773
  "app",
232662
233774
  "backend/src/modules",
232663
233775
  "backend/src/features",
232664
233776
  "frontend/src/modules",
232665
233777
  "frontend/src/features",
233778
+ "frontend/src/views",
233779
+ "src/views",
233780
+ "routes",
233781
+ "src/routes",
233782
+ "app/Modules",
233783
+ "app/services",
232666
233784
  "services",
232667
233785
  "packages",
232668
233786
  "libs"
@@ -232677,8 +233795,13 @@ function collectExplicitRoots(workspaceRoot) {
232677
233795
  continue;
232678
233796
  }
232679
233797
  for (const candidate of [
233798
+ `apps/${appEntry.name}/src`,
232680
233799
  `apps/${appEntry.name}/src/modules`,
232681
233800
  `apps/${appEntry.name}/src/features`,
233801
+ `apps/${appEntry.name}/src/domain`,
233802
+ `apps/${appEntry.name}/src/domains`,
233803
+ `apps/${appEntry.name}/src/application`,
233804
+ `apps/${appEntry.name}/src/infrastructure`,
232682
233805
  `apps/${appEntry.name}/app`,
232683
233806
  `apps/${appEntry.name}/src/app`
232684
233807
  ]) {
@@ -232687,8 +233810,70 @@ function collectExplicitRoots(workspaceRoot) {
232687
233810
  }
232688
233811
  }
232689
233812
  }
233813
+ for (const container of ["packages", "services"]) {
233814
+ const containerRoot = path53.join(workspaceRoot, container);
233815
+ for (const containerEntry of safeReadDirEntries(containerRoot)) {
233816
+ if (!containerEntry.isDirectory() || ignoredDirectoryNames4.has(containerEntry.name)) {
233817
+ continue;
233818
+ }
233819
+ for (const candidate of [
233820
+ `${container}/${containerEntry.name}/src`,
233821
+ `${container}/${containerEntry.name}/src/modules`,
233822
+ `${container}/${containerEntry.name}/src/features`,
233823
+ `${container}/${containerEntry.name}/src/domain`,
233824
+ `${container}/${containerEntry.name}/src/domains`,
233825
+ `${container}/${containerEntry.name}/src/application`,
233826
+ `${container}/${containerEntry.name}/src/infrastructure`
233827
+ ]) {
233828
+ if (pathExists15(path53.join(workspaceRoot, ...candidate.split("/")))) {
233829
+ roots.push(candidate);
233830
+ }
233831
+ }
233832
+ }
233833
+ }
232690
233834
  return uniqueSorted(roots);
232691
233835
  }
233836
+ function collectJavaPackageRoots(workspaceRoot) {
233837
+ const roots = [];
233838
+ for (const sourceRoot of ["src/main/java", "src/main/kotlin", "src"]) {
233839
+ const absoluteSourceRoot = path53.join(workspaceRoot, ...sourceRoot.split("/"));
233840
+ if (!pathExists15(absoluteSourceRoot)) {
233841
+ continue;
233842
+ }
233843
+ const queue = [
233844
+ { absolutePath: absoluteSourceRoot, relativePath: sourceRoot, depth: 0 }
233845
+ ];
233846
+ while (queue.length > 0) {
233847
+ const current = queue.shift();
233848
+ if (!current) {
233849
+ continue;
233850
+ }
233851
+ if (current.depth > 6) {
233852
+ continue;
233853
+ }
233854
+ for (const entry of safeReadDirEntries(current.absolutePath)) {
233855
+ if (!entry.isDirectory() || ignoredDirectoryNames4.has(entry.name)) {
233856
+ continue;
233857
+ }
233858
+ const childRelativePath = normalizePath13(`${current.relativePath}/${entry.name}`);
233859
+ const lowerName = entry.name.toLowerCase();
233860
+ if (lowerName === "modules" || lowerName === "features") {
233861
+ const childCount = safeReadDirEntries(path53.join(current.absolutePath, entry.name)).filter((child) => child.isDirectory() && !ignoredDirectoryNames4.has(child.name)).length;
233862
+ if (childCount >= 2) {
233863
+ roots.push(childRelativePath);
233864
+ }
233865
+ continue;
233866
+ }
233867
+ queue.push({
233868
+ absolutePath: path53.join(current.absolutePath, entry.name),
233869
+ relativePath: childRelativePath,
233870
+ depth: current.depth + 1
233871
+ });
233872
+ }
233873
+ }
233874
+ }
233875
+ return roots;
233876
+ }
232692
233877
  function collectFallbackSrcModules(workspaceRoot, context) {
232693
233878
  const srcRoot = path53.join(workspaceRoot, "src");
232694
233879
  const srcEntries = safeReadDirEntries(srcRoot).filter((entry) => entry.isDirectory() && !ignoredDirectoryNames4.has(entry.name)).sort((left, right) => left.name.localeCompare(right.name));
@@ -232726,6 +233911,14 @@ function detectModules(workspaceRoot, options) {
232726
233911
  projectKinds: options?.projectKinds,
232727
233912
  stacks: options?.stacks
232728
233913
  };
233914
+ if (options?.stacks?.some((stack) => stack === "java" || stack === "spring" || stack === "kotlin" || stack === "kotlin_spring")) {
233915
+ for (const root of collectJavaPackageRoots(workspaceRoot)) {
233916
+ if (!explicitRoots.includes(root)) {
233917
+ explicitRoots.push(root);
233918
+ }
233919
+ }
233920
+ explicitRoots.sort((left, right) => left.localeCompare(right));
233921
+ }
232729
233922
  for (const root of explicitRoots) {
232730
233923
  if (root.endsWith("/src/modules") || root === "src/modules" || root === "backend/src/modules") {
232731
233924
  evidence.push(`Found ${root} directory`);
@@ -233732,6 +234925,8 @@ function resolveImplementationProfile(input2) {
233732
234925
  return "frontend-vue";
233733
234926
  case "nestjs":
233734
234927
  return input2.apiStyleHint === "rest" ? "backend-nestjs-rest" : "backend-nestjs";
234928
+ case "express":
234929
+ return input2.apiStyleHint === "rest" ? "backend-express-rest" : "backend-express";
233735
234930
  case "spring":
233736
234931
  return input2.apiStyleHint === "rest" ? "backend-spring-rest" : "backend-spring";
233737
234932
  case "fastapi":
@@ -233748,6 +234943,14 @@ function resolveImplementationProfile(input2) {
233748
234943
  return "backend-python";
233749
234944
  case "php":
233750
234945
  return "backend-php";
234946
+ case "laravel":
234947
+ return input2.apiStyleHint === "rest" ? "backend-laravel-rest" : "backend-laravel";
234948
+ case "ruby_rails":
234949
+ return input2.apiStyleHint === "rest" ? "backend-rails-rest" : "backend-rails";
234950
+ case "kotlin":
234951
+ return "backend-kotlin";
234952
+ case "kotlin_spring":
234953
+ return input2.apiStyleHint === "rest" ? "backend-kotlin-spring-rest" : "backend-kotlin-spring";
233751
234954
  case "go":
233752
234955
  return "backend-go";
233753
234956
  case "node_typescript":
@@ -233910,7 +235113,7 @@ function resolveStackSelections(detection, projectKinds) {
233910
235113
  }
233911
235114
  if (projectKinds.includes("library")) {
233912
235115
  const selectedLibrary = detection.stacks.selectedAdapterId;
233913
- if (selectedLibrary === "terraform" || selectedLibrary === "ansible" || selectedLibrary === "node_typescript" || selectedLibrary === "python" || selectedLibrary === "go") {
235116
+ if (selectedLibrary === "terraform" || selectedLibrary === "ansible" || selectedLibrary === "node_typescript" || selectedLibrary === "python" || selectedLibrary === "go" || selectedLibrary === "kotlin") {
233914
235117
  selections.library = selectedLibrary;
233915
235118
  }
233916
235119
  }