Skip to content

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.

  1. read → usmconfig.json detection.manifests (or defaults)
  2. scan → directories for all manifest files
  3. parse → each manifest for framework dependencies
  4. classify → service type (web-app, api, worker) from framework
  5. 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.

  1. scan → .py files in service directory
  2. match → FastAPI: @app.(get|post|put|delete|patch), @router.(get|post|...); Flask: @app.route; Django: path('', views)
  3. extract → path and HTTP method from decorator/pattern
  4. 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.

  1. scan → .go files in service directory
  2. match → (r|router|mux).(GET|POST|PUT|DELETE|PATCH|HandleFunc|Handle)
  3. extract → path and HTTP method
  4. 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.

  1. scan → .rs files in service directory
  2. match → .route(), .service(), #[get(...)], #[post(...)], #[route(GET, "/path")]
  3. extract → path and HTTP method
  4. 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.

  1. scan → .java and .kt files in service directory
  2. match → Spring: @GetMapping/@PostMapping/@RequestMapping; Javalin: app.get/post; Quarkus: @GET/@POST + @Path
  3. extract → path and HTTP method from annotation
  4. 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.

  1. scan → .cs files in service directory
  2. match → [HttpGet], [HttpPost], [Route("path")], app.MapGet, app.MapPost
  3. extract → path and HTTP method from attribute
  4. 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).

  1. scan → config/routes.rb and .rb files in service directory
  2. match → Rails: get/post/resources/namespace; Sinatra: get '/path' do, post '/path' do
  3. extract → path and HTTP method
  4. 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).

  1. scan → routes/web.php, routes/api.php, and .php files
  2. match → Laravel: Route::get/post; Symfony: #[Route]; Slim: $app->get/post
  3. extract → path and HTTP method
  4. generate → routes[] in feature .usm files

Detect Elixir routes (Phoenix) (detect-elixir-routes)

Scan router.ex for Phoenix routes (get/post/pipe_through/scope).

  1. scan → lib/*_web/router.ex files
  2. match → get/post/put/patch/delete within scope blocks
  3. extract → path and HTTP method
  4. 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).

  1. scan → .swift files in service directory
  2. match → routes.get/post/put/delete, app.get/post
  3. extract → path and HTTP method
  4. 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.

  1. scan → .scala files and conf/routes files
  2. match → Akka: path/endpoints; Play: GET /path; Tapir: endpoint.get/post
  3. extract → path and HTTP method
  4. 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.

  1. scan → .cpp and .h files in service directory
  2. match → Crow: CROW_ROUTE; Drogon: registerHandler; Pistache: router.get/post
  3. extract → path and HTTP method
  4. 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.

  1. scan → known ORM schema files per language
  2. parse → model definitions (class-based, struct-based, macro-based, annotation-based)
  3. 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