diff --git a/apps/backend/project/command_registry/frameworks.py b/apps/backend/project/command_registry/frameworks.py
index 2a9c09e7e..f811f1f84 100644
--- a/apps/backend/project/command_registry/frameworks.py
+++ b/apps/backend/project/command_registry/frameworks.py
@@ -163,6 +163,22 @@
"dart",
"pub",
}, # Dart HTTP framework (deprecated but still used)
+ # JVM frameworks
+ "spring-boot": {"mvn", "gradle", "gradlew"},
+ "quarkus": {"quarkus", "mvn", "gradle"},
+ "micronaut": {"mn", "mvn", "gradle"},
+ "android": {"gradlew", "adb"},
+ "compose": {"gradle", "gradlew"}, # Jetpack/Multiplatform Compose
+ # .NET frameworks
+ "aspnet": {"dotnet"},
+ "maui": {"dotnet"},
+ "unity": {"dotnet", "msbuild"},
+ # C/C++ frameworks
+ "qt": {"qmake", "moc", "uic", "rcc"},
+ "gtest": {"ctest"},
+ "catch2": {"ctest"},
+ # Swift frameworks
+ "vapor": {"vapor", "swift"},
}
diff --git a/apps/backend/project/framework_detector.py b/apps/backend/project/framework_detector.py
index 0d4ab3532..f0b1a393f 100644
--- a/apps/backend/project/framework_detector.py
+++ b/apps/backend/project/framework_detector.py
@@ -41,6 +41,11 @@ def detect_all(self) -> list[str]:
self.detect_dart_frameworks()
self.detect_go_frameworks()
self.detect_rust_frameworks()
+ self.detect_jvm_frameworks()
+ self.detect_dotnet_frameworks()
+ self.detect_cpp_frameworks()
+ self.detect_elixir_frameworks()
+ self.detect_swift_frameworks()
return self.frameworks
def detect_nodejs_frameworks(self) -> None:
@@ -307,3 +312,97 @@ def detect_rust_frameworks(self) -> None:
for dep, framework in rust_framework_deps.items():
if dep in content:
self.frameworks.append(framework)
+
+ def detect_jvm_frameworks(self) -> None:
+ """Detect JVM frameworks from Maven/Gradle build files."""
+ content = ""
+ for build_file in (
+ "pom.xml",
+ "build.gradle",
+ "build.gradle.kts",
+ "settings.gradle",
+ "settings.gradle.kts",
+ ):
+ file_content = self.parser.read_text(build_file)
+ if file_content:
+ content += file_content
+
+ if not content:
+ return
+
+ jvm_framework_deps = {
+ "spring-boot": "spring-boot",
+ "org.springframework": "spring-boot",
+ "io.quarkus": "quarkus",
+ "io.micronaut": "micronaut",
+ "com.android.application": "android",
+ "com.android.library": "android",
+ "org.jetbrains.compose": "compose",
+ }
+
+ for dep, framework in jvm_framework_deps.items():
+ if dep in content and framework not in self.frameworks:
+ self.frameworks.append(framework)
+
+ def detect_dotnet_frameworks(self) -> None:
+ """Detect .NET frameworks from project files."""
+ csproj_files = self.parser.glob_files("*.csproj") + self.parser.glob_files(
+ "*/*.csproj"
+ )
+ content = ""
+ for csproj in csproj_files:
+ try:
+ with open(csproj, encoding="utf-8") as f:
+ content += f.read()
+ except (OSError, UnicodeDecodeError):
+ continue
+
+ if content:
+ if "Microsoft.AspNetCore" in content or "Microsoft.NET.Sdk.Web" in content:
+ self.frameworks.append("aspnet")
+ if "Microsoft.Maui" in content or "UseMaui" in content:
+ self.frameworks.append("maui")
+
+ # Unity projects carry a ProjectVersion.txt under ProjectSettings/
+ if self.parser.file_exists("ProjectSettings/ProjectVersion.txt"):
+ self.frameworks.append("unity")
+
+ def detect_cpp_frameworks(self) -> None:
+ """Detect C/C++ frameworks and test libraries from CMake files."""
+ content = self.parser.read_text("CMakeLists.txt")
+ if not content:
+ return
+
+ cpp_framework_markers = {
+ "Qt5": "qt",
+ "Qt6": "qt",
+ "find_package(Qt": "qt",
+ "Boost": "boost",
+ "GTest": "gtest",
+ "googletest": "gtest",
+ "Catch2": "catch2",
+ }
+
+ for marker, framework in cpp_framework_markers.items():
+ if marker in content and framework not in self.frameworks:
+ self.frameworks.append(framework)
+
+ def detect_elixir_frameworks(self) -> None:
+ """Detect Elixir frameworks from mix.exs."""
+ content = self.parser.read_text("mix.exs")
+ if not content:
+ return
+
+ if ":phoenix" in content:
+ self.frameworks.append("phoenix")
+ if ":ecto" in content and "ecto" not in self.frameworks:
+ self.frameworks.append("ecto")
+
+ def detect_swift_frameworks(self) -> None:
+ """Detect Swift frameworks from Package.swift."""
+ content = self.parser.read_text("Package.swift")
+ if not content:
+ return
+
+ if "vapor" in content.lower():
+ self.frameworks.append("vapor")
diff --git a/tests/test_project_analyzer.py b/tests/test_project_analyzer.py
index e270601da..a6fce4b5e 100644
--- a/tests/test_project_analyzer.py
+++ b/tests/test_project_analyzer.py
@@ -317,6 +317,99 @@ def test_detects_pytest(self, temp_dir: Path):
assert "pytest" in analyzer.profile.detected_stack.frameworks
+ def test_detects_spring_boot_from_pom(self, temp_dir: Path):
+ """Detects Spring Boot from pom.xml."""
+ (temp_dir / "pom.xml").write_text(
+ "org.springframework.boot"
+ "spring-boot-starter-parent"
+ )
+
+ analyzer = ProjectAnalyzer(temp_dir)
+ analyzer._detect_frameworks()
+
+ assert "spring-boot" in analyzer.profile.detected_stack.frameworks
+
+ def test_detects_android_from_gradle_kts(self, temp_dir: Path):
+ """Detects Android from Gradle Kotlin-DSL plugin."""
+ (temp_dir / "build.gradle.kts").write_text(
+ 'plugins { id("com.android.application") }'
+ )
+
+ analyzer = ProjectAnalyzer(temp_dir)
+ analyzer._detect_frameworks()
+
+ assert "android" in analyzer.profile.detected_stack.frameworks
+
+ def test_detects_quarkus_from_gradle(self, temp_dir: Path):
+ """Detects Quarkus from build.gradle."""
+ (temp_dir / "build.gradle").write_text(
+ "plugins { id 'io.quarkus' version '3.0.0' }"
+ )
+
+ analyzer = ProjectAnalyzer(temp_dir)
+ analyzer._detect_frameworks()
+
+ assert "quarkus" in analyzer.profile.detected_stack.frameworks
+
+ def test_detects_aspnet_from_csproj(self, temp_dir: Path):
+ """Detects ASP.NET Core from csproj SDK."""
+ (temp_dir / "App.csproj").write_text(
+ ''
+ )
+
+ analyzer = ProjectAnalyzer(temp_dir)
+ analyzer._detect_frameworks()
+
+ assert "aspnet" in analyzer.profile.detected_stack.frameworks
+
+ def test_detects_unity_from_project_version(self, temp_dir: Path):
+ """Detects Unity from ProjectSettings/ProjectVersion.txt."""
+ settings = temp_dir / "ProjectSettings"
+ settings.mkdir()
+ (settings / "ProjectVersion.txt").write_text("m_EditorVersion: 2022.3.0f1")
+
+ analyzer = ProjectAnalyzer(temp_dir)
+ analyzer._detect_frameworks()
+
+ assert "unity" in analyzer.profile.detected_stack.frameworks
+
+ def test_detects_qt_and_gtest_from_cmake(self, temp_dir: Path):
+ """Detects Qt and GTest from CMakeLists.txt."""
+ (temp_dir / "CMakeLists.txt").write_text(
+ "project(demo)\n"
+ "find_package(Qt6 REQUIRED COMPONENTS Widgets)\n"
+ "find_package(GTest REQUIRED)\n"
+ )
+
+ analyzer = ProjectAnalyzer(temp_dir)
+ analyzer._detect_frameworks()
+
+ assert "qt" in analyzer.profile.detected_stack.frameworks
+ assert "gtest" in analyzer.profile.detected_stack.frameworks
+
+ def test_detects_phoenix_from_mix(self, temp_dir: Path):
+ """Detects Phoenix from mix.exs deps."""
+ (temp_dir / "mix.exs").write_text(
+ 'defp deps do [{:phoenix, "~> 1.7"}, {:ecto, "~> 3.10"}] end'
+ )
+
+ analyzer = ProjectAnalyzer(temp_dir)
+ analyzer._detect_frameworks()
+
+ assert "phoenix" in analyzer.profile.detected_stack.frameworks
+ assert "ecto" in analyzer.profile.detected_stack.frameworks
+
+ def test_detects_vapor_from_package_swift(self, temp_dir: Path):
+ """Detects Vapor from Package.swift."""
+ (temp_dir / "Package.swift").write_text(
+ '.package(url: "https://github.com/vapor/vapor.git", from: "4.0.0")'
+ )
+
+ analyzer = ProjectAnalyzer(temp_dir)
+ analyzer._detect_frameworks()
+
+ assert "vapor" in analyzer.profile.detected_stack.frameworks
+
class TestDatabaseDetection:
"""Tests for database detection."""