usm/cli-multi-lang-scan [planned]
Multi-language scanner support — extend usm scan to detect services, routes, and data models from 10+ languages and 30+ frameworks. The .usm format and all generators are already language-agnostic; only the scanner needs extending.
Status: planned
Intent
The .usm format, MCP tools, generators, and validator are all language-agnostic. But the scanner only detects Node.js/TypeScript projects (package.json, Next.js routes, Prisma schemas). A Python team using FastAPI, a Java team using Spring Boot, a C# team using ASP.NET, or a Ruby team using Rails can't run 'usm scan' — they have to hand-write .usm files. This feature extends the scanner to detect services and routes from common frameworks across all major programming languages, making USM truly language-agnostic end-to-end.
Decisions
manifest-based-detection [accepted]
Decision: Detect services by language manifest files
Rationale: Every language ecosystem has a standard manifest file. Detecting these files is reliable, language-specific, and doesn't require parsing source code. The manifest also tells us dependencies (framework detection).
Consequences: Scanner needs a manifest-to-language map. New languages added by adding a manifest entry.
framework-specific-route-detection [accepted]
Decision: Detect routes per-framework, not per-language
Rationale: Route patterns differ by framework, not language. Next.js uses app/page.tsx, FastAPI uses @app.get decorators, Spring uses @GetMapping annotations, Rails uses config/routes.rb, ASP.NET uses [HttpGet] attributes. Each framework needs its own route extractor.
Alternatives considered:
- Generic AST parsing per language — rejected: Too complex, requires language-specific parsers, fragile
- File-pattern matching only (no framework awareness) — rejected: Misses decorator/annotation-based routes (FastAPI, Spring, ASP.NET)
Consequences: Route detection is framework-by-framework. New frameworks need a route extractor.
configurable-in-usmconfig [accepted]
Decision: Language/framework detection rules are configurable in usmconfig.json
Rationale: Users can add custom manifest patterns, route patterns, and data model patterns for frameworks we don't support out of the box. Defaults cover common frameworks; advanced users can extend.
Consequences: usmconfig.json gains a 'detection' section with language/framework rules
all-languages-from-start [accepted]
Decision: Support all major languages from the initial implementation
Rationale: Rather than phasing, include all major languages (Python, Go, Rust, Java, Kotlin, C#, Ruby, PHP, Elixir, Swift, Scala, C/C++) from the start. The manifest detection is simple (file pattern matching) and route detection is regex-based per framework. The complexity is manageable.
Consequences: Larger initial implementation but no phased rollout complexity
Flows
Detect services from multiple language manifests (detect-multi-lang-services)
Scanner reads usmconfig.json detection.manifests (or defaults) and checks for manifest files. Each manifest identifies a service and its language. Framework is detected from dependencies in the manifest.
- read → usmconfig.json detection.manifests (or defaults)
- scan → directories for all manifest files
- parse → each manifest for framework dependencies
- classify → service type (web-app, api, worker) from framework
- generate → .usm/services/<name>.usm with detected language, runtime, framework
Detect Python routes (FastAPI, Flask, Django) (detect-python-routes)
Scan .py files for decorator-based routes. FastAPI uses @app.get/@router.get, Flask uses @app.route, Django uses urlpatterns in urls.py.
- scan → .py files in service directory
- match → FastAPI: @app.(get|post|put|delete|patch), @router.(get|post|...); Flask: @app.route; Django: path('', views)
- extract → path and HTTP method from decorator/pattern
- generate → routes[] in feature .usm files
Detect Go routes (chi, gin, echo, net/http) (detect-go-routes)
Scan .go files for router registration patterns. chi/gin/echo use r.GET/r.POST, net/http uses http.HandleFunc, gorilla/mux uses r.HandleFunc.
- scan → .go files in service directory
- match → (r|router|mux).(GET|POST|PUT|DELETE|PATCH|HandleFunc|Handle)
- extract → path and HTTP method
- generate → routes[] in feature .usm files
Detect Rust routes (Axum, Actix, Rocket) (detect-rust-routes)
Scan .rs files for route patterns. Axum uses .route(), Actix uses #[get(...)] macros, Rocket uses #[get("/path")] attributes.
- scan → .rs files in service directory
- match → .route(), .service(), #[get(...)], #[post(...)], #[route(GET, "/path")]
- extract → path and HTTP method
- generate → routes[] in feature .usm files
Detect Java/Kotlin routes (Spring Boot, Javalin, Quarkus) (detect-java-routes)
Scan .java/.kt files for annotation-based routes. Spring uses @GetMapping/@PostMapping/@RequestMapping, Javalin uses app.get/post, Quarkus uses @Path + @GET/@POST.
- scan → .java and .kt files in service directory
- match → Spring: @GetMapping/@PostMapping/@RequestMapping; Javalin: app.get/post; Quarkus: @GET/@POST + @Path
- extract → path and HTTP method from annotation
- generate → routes[] in feature .usm files
Detect C# routes (ASP.NET Core, Minimal APIs) (detect-csharp-routes)
Scan .cs files for attribute-based routes. ASP.NET uses [HttpGet], [HttpPost], [Route], Minimal APIs use app.MapGet/MapPost.
- scan → .cs files in service directory
- match → [HttpGet], [HttpPost], [Route("path")], app.MapGet, app.MapPost
- extract → path and HTTP method from attribute
- generate → routes[] in feature .usm files
Detect Ruby routes (Rails, Sinatra) (detect-ruby-routes)
Scan config/routes.rb for Rails routes (get/post/resources) and .rb files for Sinatra routes (get '/path' do).
- scan → config/routes.rb and .rb files in service directory
- match → Rails: get/post/resources/namespace; Sinatra: get '/path' do, post '/path' do
- extract → path and HTTP method
- generate → routes[] in feature .usm files
Detect PHP routes (Laravel, Symfony, Slim) (detect-php-routes)
Scan routes/web.php for Laravel routes (Route::get/post), .php files for Symfony attributes (#[Route]) and Slim ($app->get/post).
- scan → routes/web.php, routes/api.php, and .php files
- match → Laravel: Route::get/post; Symfony: #[Route]; Slim: $app->get/post
- extract → path and HTTP method
- generate → routes[] in feature .usm files
Detect Elixir routes (Phoenix) (detect-elixir-routes)
Scan router.ex for Phoenix routes (get/post/pipe_through/scope).
- scan → lib/*_web/router.ex files
- match → get/post/put/patch/delete within scope blocks
- extract → path and HTTP method
- generate → routes[] in feature .usm files
Detect Swift routes (Vapor) (detect-swift-routes)
Scan .swift files for Vapor route registrations (routes.get/post, app.get/post).
- scan → .swift files in service directory
- match → routes.get/post/put/delete, app.get/post
- extract → path and HTTP method
- generate → routes[] in feature .usm files
Detect Scala routes (Akka HTTP, Play, Tapir) (detect-scala-routes)
Scan .scala files for route patterns. Akka HTTP uses path/endpoints, Play uses routes file, Tapir uses endpoint.get/post.
- scan → .scala files and conf/routes files
- match → Akka: path/endpoints; Play: GET /path; Tapir: endpoint.get/post
- extract → path and HTTP method
- generate → routes[] in feature .usm files
Detect C++ routes (Crow, Drogon, Pistache) (detect-cpp-routes)
Scan .cpp/.h files for route registration patterns. Crow uses CROW_ROUTE, Drogon uses app.registerHandler, Pistache uses router.get/post.
- scan → .cpp and .h files in service directory
- match → Crow: CROW_ROUTE; Drogon: registerHandler; Pistache: router.get/post
- extract → path and HTTP method
- generate → routes[] in feature .usm files
Detect data models from multiple ORMs across languages (detect-multi-lang-data-models)
Extend data model detection beyond Prisma to support ORMs across all supported languages.
- scan → known ORM schema files per language
- parse → model definitions (class-based, struct-based, macro-based, annotation-based)
- generate → .usm/data/<name>.usm with models and fields
Contracts
manifest-detection-coverage
Scanner detects services from all supported language manifests
Acceptance criteria:
- [ ] package.json → Node.js/TypeScript/JavaScript
- [ ] pyproject.toml or requirements.txt → Python
- [ ] Cargo.toml → Rust
- [ ] go.mod → Go
- [ ] pom.xml or build.gradle → Java/Kotlin
- [ ] .csproj or .sln → C#/.NET
- [ ] Gemfile → Ruby
- [ ] composer.json → PHP
- [ ] mix.exs → Elixir
- [ ] Package.swift → Swift
- [ ] build.sbt → Scala
- [ ] CMakeLists.txt or Makefile → C/C++
- [ ] Framework detected from dependencies in manifest
route-detection-coverage
Scanner detects routes from 30+ frameworks across all languages
Acceptance criteria:
- [ ] Next.js: app/page.tsx, app/route.ts (existing)
- [ ] Express: app.get/post/put/delete in .js/.ts files
- [ ] FastAPI: @app.get/post decorators in .py files
- [ ] Flask: @app.route decorators in .py files
- [ ] Django: path() patterns in urls.py
- [ ] Go chi/gin/echo: r.GET/POST and router.HandleFunc patterns
- [ ] Go net/http: http.HandleFunc patterns
- [ ] Rust Axum: .route() calls
- [ ] Rust Actix: #[get/post] macros
- [ ] Rust Rocket: #[get/post] attributes
- [ ] Spring Boot: @GetMapping/@PostMapping/@RequestMapping annotations
- [ ] Javalin: app.get/post calls
- [ ] Quarkus: @GET/@POST + @Path annotations
- [ ] ASP.NET Core: [HttpGet]/[HttpPost] attributes
- [ ] ASP.NET Minimal: app.MapGet/MapPost
- [ ] Rails: get/post/resources in config/routes.rb
- [ ] Sinatra: get '/path' do in .rb files
- [ ] Laravel: Route::get/post in routes/web.php
- [ ] Symfony: #[Route] attributes
- [ ] Slim: $app->get/post in .php files
- [ ] Phoenix: get/post in router.ex
- [ ] Vapor: routes.get/post in .swift files
- [ ] Akka HTTP: path/endpoints in .scala files
- [ ] Play: GET /path in conf/routes
- [ ] Tapir: endpoint.get/post in .scala files
- [ ] Crow: CROW_ROUTE macro in .cpp files
- [ ] Drogon: registerHandler in .cpp files
- [ ] Pistache: router.get/post in .cpp files
data-model-detection-coverage
Scanner detects data models from ORMs across languages
Acceptance criteria:
- [ ] Prisma: schema.prisma (existing, TypeScript)
- [ ] SQLAlchemy: class definitions in models.py (Python)
- [ ] Django ORM: class definitions in models.py (Python)
- [ ] GORM: struct definitions with gorm tags (Go)
- [ ] Diesel: table! macros in schema.rs (Rust)
- [ ] Hibernate: @Entity annotations in .java (Java)
- [ ] Entity Framework: DbSet properties in DbContext (C#)
- [ ] ActiveRecord: class definitions inheriting ApplicationRecord (Ruby)
- [ ] Eloquent: class definitions extending Model (PHP)
- [ ] Ecto: schema definitions in .ex files (Elixir)
config-extensible
Detection rules are configurable in usmconfig.json
Acceptance criteria:
- [ ] detection.manifests array with {pattern, language, frameworks?} objects
- [ ] detection.routes array with {framework, pattern, method_group, path_group} objects
- [ ] detection.data_models array with {orm, pattern, model_pattern?} objects
- [ ] Defaults cover all supported frameworks; users can add custom patterns
backward-compatible
Existing Node.js/TypeScript scanning unchanged
Acceptance criteria:
- [ ] usm scan on a Node.js project produces same results as before
- [ ] usmconfig.json without detection section uses defaults
Tests
detect-python-service
Given:
- pyproject_toml_with_fastapi: true
Then:
- assertion: .usm/services/<name>.usm created with runtime: python
- assertion: framework: fastapi detected
detect-go-service
Given:
- go_mod_with_gin: true
Then:
- assertion: .usm/services/<name>.usm created with runtime: go
- assertion: framework: gin detected
detect-rust-service
Given:
- cargo_toml_with_axum: true
Then:
- assertion: .usm/services/<name>.usm created with runtime: rust
- assertion: framework: axum detected
detect-java-service
Given:
- pom_xml_with_spring_boot: true
Then:
- assertion: .usm/services/<name>.usm created with runtime: java
- assertion: framework: spring-boot detected
detect-csharp-service
Given:
- csproj_with_aspnet: true
Then:
- assertion: .usm/services/<name>.usm created with runtime: dotnet
- assertion: framework: aspnet-core detected
detect-ruby-service
Given:
- gemfile_with_rails: true
Then:
- assertion: .usm/services/<name>.usm created with runtime: ruby
- assertion: framework: rails detected
detect-php-service
Given:
- composer_json_with_laravel: true
Then:
- assertion: .usm/services/<name>.usm created with runtime: php
- assertion: framework: laravel detected
detect-elixir-service
Given:
- mix_exs_with_phoenix: true
Then:
- assertion: .usm/services/<name>.usm created with runtime: elixir
- assertion: framework: phoenix detected
detect-swift-service
Given:
- package_swift_with_vapor: true
Then:
- assertion: .usm/services/<name>.usm created with runtime: swift
- assertion: framework: vapor detected
detect-scala-service
Given:
- build_sbt_with_akka: true
Then:
- assertion: .usm/services/<name>.usm created with runtime: scala
- assertion: framework: akka-http detected
detect-cpp-service
Given:
- cmake_with_drogon: true
Then:
- assertion: .usm/services/<name>.usm created with runtime: cpp
- assertion: framework: drogon detected
extract-fastapi-routes
Given:
- python_file_with_decorators: true
Then:
- assertion: routes[] extracted with correct paths and methods
extract-spring-routes
Given:
- java_file_with_annotations: true
Then:
- assertion: routes[] extracted from @GetMapping/@PostMapping
extract-aspnet-routes
Given:
- cs_file_with_attributes: true
Then:
- assertion: routes[] extracted from [HttpGet]/[HttpPost]
extract-rails-routes
Given:
- routes_rb_with_get_post: true
Then:
- assertion: routes[] extracted from get/post/resources
extract-laravel-routes
Given:
- web_php_with_route_get: true
Then:
- assertion: routes[] extracted from Route::get/post
extract-phoenix-routes
Given:
- router_ex_with_get_post: true
Then:
- assertion: routes[] extracted from get/post in scope blocks
backward-compatible-node
Given:
- package_json_with_nextjs: true
Then:
- assertion: same results as before this feature
custom-detection-rules
Given:
- usmconfig_with_custom_manifest: true
Then:
- assertion: custom manifest pattern detected
Implementation
- Primary: src/scan/structural.ts
- Test code status: none
See Also
- usm/cli-scan
- usm/cli-init