@archpilotlabs/archpilot 0.0.8 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -48,7 +48,46 @@ No runtime agents. No external services. Fully deterministic and local-first.
48
48
 
49
49
  ---
50
50
 
51
- ## Local-first. Cloud-powered.
51
+ ## 🧩 Supported Stacks
52
+
53
+ ArchPilot supports **practical deterministic architecture detection** across modern application stacks.
54
+
55
+ ### Backend
56
+
57
+ * Node.js / TypeScript
58
+ * NestJS
59
+ * Express.js
60
+ * Java
61
+ * Spring / Spring Boot
62
+ * Kotlin
63
+ * FastAPI
64
+ * Django
65
+ * Flask
66
+ * PHP
67
+ * Laravel
68
+ * Go
69
+ * .NET
70
+ * Ruby on Rails
71
+
72
+ ### Frontend
73
+
74
+ * React
75
+ * Next.js
76
+ * Angular
77
+ * Vue
78
+
79
+ ### Infrastructure
80
+
81
+ * Terraform
82
+ * Ansible
83
+
84
+ ArchPilot detects stack shape from practical repository evidence - framework markers, manifests, project layout, and source structure - then applies conservative architecture discovery defaults locally.
85
+
86
+ **No cloud scanning. No remote indexing. Deterministic local validation.**
87
+
88
+ ---
89
+
90
+ ## ☁️ Local-first. Cloud-powered.
52
91
 
53
92
  Start locally in minutes:
54
93
 
@@ -208,7 +247,7 @@ archpilot governance upload
208
247
 
209
248
  ---
210
249
 
211
- ## 🧩 Core Capabilities
250
+ ## ⚙️ Core Capabilities
212
251
 
213
252
  * Architecture-as-code (`.archpilot/`)
214
253
  * Dependency validation and module contracts
@@ -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) {
@@ -231635,6 +231992,7 @@ var ignoredDirectoryNames2 = /* @__PURE__ */ new Set([
231635
231992
  ]);
231636
231993
  var backendStackIds = /* @__PURE__ */ new Set([
231637
231994
  "spring",
231995
+ "express",
231638
231996
  "fastapi",
231639
231997
  "django",
231640
231998
  "flask",
@@ -231643,7 +232001,11 @@ var backendStackIds = /* @__PURE__ */ new Set([
231643
232001
  "java",
231644
232002
  "python",
231645
232003
  "php",
232004
+ "laravel",
231646
232005
  "go",
232006
+ "ruby_rails",
232007
+ "kotlin",
232008
+ "kotlin_spring",
231647
232009
  "node_typescript",
231648
232010
  "node_javascript"
231649
232011
  ]);
@@ -231861,6 +232223,7 @@ function buildDetectionContext(workspaceRoot, filePaths, pathsByFileName, extens
231861
232223
  function toDetectedStack(adapterId) {
231862
232224
  switch (adapterId) {
231863
232225
  case "node_typescript":
232226
+ case "express":
231864
232227
  case "nestjs":
231865
232228
  case "java":
231866
232229
  case "spring":
@@ -231870,7 +232233,11 @@ function toDetectedStack(adapterId) {
231870
232233
  case "dotnet":
231871
232234
  case "python":
231872
232235
  case "php":
232236
+ case "laravel":
231873
232237
  case "go":
232238
+ case "ruby_rails":
232239
+ case "kotlin":
232240
+ case "kotlin_spring":
231874
232241
  case "react":
231875
232242
  case "nextjs":
231876
232243
  case "angular":
@@ -231891,6 +232258,9 @@ function resolveLanguages(stacks) {
231891
232258
  if (stacks.includes("spring")) {
231892
232259
  languages.add("java");
231893
232260
  }
232261
+ if (stacks.includes("kotlin") || stacks.includes("kotlin_spring")) {
232262
+ languages.add("kotlin");
232263
+ }
231894
232264
  if (stacks.includes("dotnet")) {
231895
232265
  languages.add("csharp");
231896
232266
  }
@@ -231909,6 +232279,12 @@ function resolveLanguages(stacks) {
231909
232279
  if (stacks.includes("php")) {
231910
232280
  languages.add("php");
231911
232281
  }
232282
+ if (stacks.includes("laravel")) {
232283
+ languages.add("php");
232284
+ }
232285
+ if (stacks.includes("ruby_rails")) {
232286
+ languages.add("ruby");
232287
+ }
231912
232288
  if (stacks.includes("go")) {
231913
232289
  languages.add("go");
231914
232290
  }
@@ -231922,6 +232298,12 @@ function resolveLanguages(stacks) {
231922
232298
  languages.add("typescript");
231923
232299
  languages.add("javascript");
231924
232300
  }
232301
+ if (stacks.includes("express")) {
232302
+ languages.add("javascript");
232303
+ if (stacks.includes("node_typescript")) {
232304
+ languages.add("typescript");
232305
+ }
232306
+ }
231925
232307
  if (stacks.includes("react") || stacks.includes("nextjs") || stacks.includes("angular") || stacks.includes("vue")) {
231926
232308
  languages.add(stacks.includes("node_typescript") ? "typescript" : "javascript");
231927
232309
  }
@@ -232399,6 +232781,7 @@ var sourceFileExtensions = /* @__PURE__ */ new Set([
232399
232781
  ".tsx",
232400
232782
  ".js",
232401
232783
  ".jsx",
232784
+ ".vue",
232402
232785
  ".java",
232403
232786
  ".py",
232404
232787
  ".php",
@@ -232433,7 +232816,9 @@ var roleFileHints = [
232433
232816
  "layout.tsx",
232434
232817
  "layout.jsx",
232435
232818
  "route.ts",
232436
- "route.js"
232819
+ "route.js",
232820
+ "view.vue",
232821
+ "page.vue"
232437
232822
  ];
232438
232823
  function normalizePath13(value) {
232439
232824
  return value.replaceAll("\\", "/");
@@ -232663,6 +233048,12 @@ function collectExplicitRoots(workspaceRoot) {
232663
233048
  "backend/src/features",
232664
233049
  "frontend/src/modules",
232665
233050
  "frontend/src/features",
233051
+ "frontend/src/views",
233052
+ "src/views",
233053
+ "routes",
233054
+ "src/routes",
233055
+ "app/Modules",
233056
+ "app/services",
232666
233057
  "services",
232667
233058
  "packages",
232668
233059
  "libs"
@@ -232689,6 +233080,47 @@ function collectExplicitRoots(workspaceRoot) {
232689
233080
  }
232690
233081
  return uniqueSorted(roots);
232691
233082
  }
233083
+ function collectJavaPackageRoots(workspaceRoot) {
233084
+ const roots = [];
233085
+ for (const sourceRoot of ["src/main/java", "src/main/kotlin", "src"]) {
233086
+ const absoluteSourceRoot = path53.join(workspaceRoot, ...sourceRoot.split("/"));
233087
+ if (!pathExists15(absoluteSourceRoot)) {
233088
+ continue;
233089
+ }
233090
+ const queue = [
233091
+ { absolutePath: absoluteSourceRoot, relativePath: sourceRoot, depth: 0 }
233092
+ ];
233093
+ while (queue.length > 0) {
233094
+ const current = queue.shift();
233095
+ if (!current) {
233096
+ continue;
233097
+ }
233098
+ if (current.depth > 6) {
233099
+ continue;
233100
+ }
233101
+ for (const entry of safeReadDirEntries(current.absolutePath)) {
233102
+ if (!entry.isDirectory() || ignoredDirectoryNames4.has(entry.name)) {
233103
+ continue;
233104
+ }
233105
+ const childRelativePath = normalizePath13(`${current.relativePath}/${entry.name}`);
233106
+ const lowerName = entry.name.toLowerCase();
233107
+ if (lowerName === "modules" || lowerName === "features") {
233108
+ const childCount = safeReadDirEntries(path53.join(current.absolutePath, entry.name)).filter((child) => child.isDirectory() && !ignoredDirectoryNames4.has(child.name)).length;
233109
+ if (childCount >= 2) {
233110
+ roots.push(childRelativePath);
233111
+ }
233112
+ continue;
233113
+ }
233114
+ queue.push({
233115
+ absolutePath: path53.join(current.absolutePath, entry.name),
233116
+ relativePath: childRelativePath,
233117
+ depth: current.depth + 1
233118
+ });
233119
+ }
233120
+ }
233121
+ }
233122
+ return roots;
233123
+ }
232692
233124
  function collectFallbackSrcModules(workspaceRoot, context) {
232693
233125
  const srcRoot = path53.join(workspaceRoot, "src");
232694
233126
  const srcEntries = safeReadDirEntries(srcRoot).filter((entry) => entry.isDirectory() && !ignoredDirectoryNames4.has(entry.name)).sort((left, right) => left.name.localeCompare(right.name));
@@ -232726,6 +233158,14 @@ function detectModules(workspaceRoot, options) {
232726
233158
  projectKinds: options?.projectKinds,
232727
233159
  stacks: options?.stacks
232728
233160
  };
233161
+ if (options?.stacks?.some((stack) => stack === "java" || stack === "spring" || stack === "kotlin" || stack === "kotlin_spring")) {
233162
+ for (const root of collectJavaPackageRoots(workspaceRoot)) {
233163
+ if (!explicitRoots.includes(root)) {
233164
+ explicitRoots.push(root);
233165
+ }
233166
+ }
233167
+ explicitRoots.sort((left, right) => left.localeCompare(right));
233168
+ }
232729
233169
  for (const root of explicitRoots) {
232730
233170
  if (root.endsWith("/src/modules") || root === "src/modules" || root === "backend/src/modules") {
232731
233171
  evidence.push(`Found ${root} directory`);
@@ -233732,6 +234172,8 @@ function resolveImplementationProfile(input2) {
233732
234172
  return "frontend-vue";
233733
234173
  case "nestjs":
233734
234174
  return input2.apiStyleHint === "rest" ? "backend-nestjs-rest" : "backend-nestjs";
234175
+ case "express":
234176
+ return input2.apiStyleHint === "rest" ? "backend-express-rest" : "backend-express";
233735
234177
  case "spring":
233736
234178
  return input2.apiStyleHint === "rest" ? "backend-spring-rest" : "backend-spring";
233737
234179
  case "fastapi":
@@ -233748,6 +234190,14 @@ function resolveImplementationProfile(input2) {
233748
234190
  return "backend-python";
233749
234191
  case "php":
233750
234192
  return "backend-php";
234193
+ case "laravel":
234194
+ return input2.apiStyleHint === "rest" ? "backend-laravel-rest" : "backend-laravel";
234195
+ case "ruby_rails":
234196
+ return input2.apiStyleHint === "rest" ? "backend-rails-rest" : "backend-rails";
234197
+ case "kotlin":
234198
+ return "backend-kotlin";
234199
+ case "kotlin_spring":
234200
+ return input2.apiStyleHint === "rest" ? "backend-kotlin-spring-rest" : "backend-kotlin-spring";
233751
234201
  case "go":
233752
234202
  return "backend-go";
233753
234203
  case "node_typescript":
@@ -233910,7 +234360,7 @@ function resolveStackSelections(detection, projectKinds) {
233910
234360
  }
233911
234361
  if (projectKinds.includes("library")) {
233912
234362
  const selectedLibrary = detection.stacks.selectedAdapterId;
233913
- if (selectedLibrary === "terraform" || selectedLibrary === "ansible" || selectedLibrary === "node_typescript" || selectedLibrary === "python" || selectedLibrary === "go") {
234363
+ if (selectedLibrary === "terraform" || selectedLibrary === "ansible" || selectedLibrary === "node_typescript" || selectedLibrary === "python" || selectedLibrary === "go" || selectedLibrary === "kotlin") {
233914
234364
  selections.library = selectedLibrary;
233915
234365
  }
233916
234366
  }
package/package.json CHANGED
@@ -1,34 +1,34 @@
1
- {
2
- "name": "@archpilotlabs/archpilot",
3
- "version": "0.0.8",
4
- "description": "Executable architecture governance CLI",
5
- "homepage": "https://archpilot.org",
6
- "bugs": {
7
- "url": "https://archpilot.org/contact"
8
- },
9
- "license": "SEE LICENSE IN LICENSE",
10
- "bin": {
11
- "archpilot": "./dist/archpilot-cli.js"
12
- },
13
- "main": "./dist/archpilot-cli.js",
14
- "files": [
15
- "dist"
16
- ],
17
- "keywords": [
18
- "architecture",
19
- "architecture-validation",
20
- "governance",
21
- "cli",
22
- "dependency-validation",
23
- "adr",
24
- "ci",
25
- "developer-tools"
26
- ],
27
- "scripts": {
28
- "build": "npm run compile",
29
- "compile": "npm run check-types && npm run lint && node esbuild.js",
30
- "watch": "node esbuild.js --watch",
31
- "check-types": "tsc --noEmit -p tsconfig.json",
32
- "lint": "eslint src"
33
- }
34
- }
1
+ {
2
+ "name": "@archpilotlabs/archpilot",
3
+ "version": "0.0.9",
4
+ "description": "Executable architecture governance CLI",
5
+ "homepage": "https://archpilot.org",
6
+ "bugs": {
7
+ "url": "https://archpilot.org/contact"
8
+ },
9
+ "license": "SEE LICENSE IN LICENSE",
10
+ "bin": {
11
+ "archpilot": "./dist/archpilot-cli.js"
12
+ },
13
+ "main": "./dist/archpilot-cli.js",
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "keywords": [
18
+ "architecture",
19
+ "architecture-validation",
20
+ "governance",
21
+ "cli",
22
+ "dependency-validation",
23
+ "adr",
24
+ "ci",
25
+ "developer-tools"
26
+ ],
27
+ "scripts": {
28
+ "build": "npm run compile",
29
+ "compile": "npm run check-types && npm run lint && node esbuild.js",
30
+ "watch": "node esbuild.js --watch",
31
+ "check-types": "tsc --noEmit -p tsconfig.json",
32
+ "lint": "eslint src"
33
+ }
34
+ }