Repowise ships language-aware resolvers under
packages/core/src/repowise/core/ingestion/resolvers/. Every language
lands on one rung of a five-rung ladder, and the rung decides which
pipeline stages produce meaningful output. Languages below the parsing
rungs still appear in the documentation and still carry
git history.
Coverage matrix
| Tier | Languages | What you get |
|---|---|---|
| Full (14) | Python · TypeScript · JavaScript · Svelte · Vue · Astro · Java · Kotlin · Go · Rust · C++ · C# · Scala · Ruby | The whole pipeline: AST symbols, import resolution, a resolved call graph, heritage, docstrings, framework edges, and code-health markers |
| Good (11) | C · Swift · PHP · Dart · Object Pascal · COBOL · GDScript · VB.NET · Elixir · F# · Objective-C | Everything above except the full health suite. Dart, Object Pascal and PHP get health markers; C, Swift, F# and Objective-C get the complexity-derived ones; COBOL, GDScript, VB.NET and Elixir get none yet |
| Partial (2) | Luau / Roblox · Razor / Blazor | Luau: AST symbols and require() resolution, Rojo and .luaurc aware, no health markers yet. Razor: a component symbol per file, call edges from @code blocks and component tags, C# health markers, no import resolution yet |
| Lightweight (6) | Clojure · Haskell · Lean 4 · Erlang · HTML · QML | A real file-to-file import graph, no symbol-level claims |
| Structural (8) | R · Zig · Julia · Elm · OCaml · Crystal · Nim · D | Git history only: blame, hotspots, co-change. No AST parsing |
Tree-sitter parsing stops after the Partial rung. The first three rungs are the 27 languages parsed to a full AST; all five rungs are the 41 on the ladder. Both numbers are worth stating and neither is worth stating alone, so the useful thing to take from this page is the rung your language sits on rather than either count.
SQL and shell sit outside the ladder on purpose, because each has a
coverage shape the rungs cannot describe. SQL is parsed by sqlglot
rather than tree-sitter and gets symbols, wiki pages and health markers
but no call graph, with import edges only inside a dbt project. Shell
gets symbols, source edges and function-level complexity, but
reachability means little for a script invoked by name.
What each tier gets from the pipeline
| Stage | Full | Good | Partial | Lightweight | Structural |
|---|---|---|---|---|---|
| File discovery and git history | yes | yes | yes | yes | yes |
| AST symbol extraction | yes | yes | yes | no | no |
| Import resolution | yes | yes | Luau only | file-level | no |
| Call graph edges | yes | yes | yes | no | no |
| Heritage (extends / implements) | yes | yes | no | no | no |
| Named bindings | yes | yes | no | no | no |
| Code-health markers | yes | Dart, Object Pascal, PHP; complexity only for C, Swift, F#, Objective-C | Razor | no | no |
| Dead code detection | yes | yes, except COBOL | yes | yes | yes |
| Semantic search and wiki pages | yes | yes | yes | yes | yes |
Scala's import resolution is partial: it shares the JVM index with Java and Kotlin and falls back to parsing SBT or Mill build files. Every other Full and Good language resolves imports outright.
COBOL is excluded from dead-code claims. JCL, schedulers and dynamic program calls are external entry paths the repository graph cannot observe, so a COBOL program that nothing in the repo calls is not evidence that nothing calls it.
GDScript has a dedicated import resolver and Godot-specific framework
edges, but no named bindings. Scenes, autoloads and class_name
declarations connect scripts that no import statement ever linked.
Svelte, Vue and Astro share the TypeScript resolver: a single-file
component's <script> blocks (and an Astro file's frontmatter) and markup
expressions are projected to a TypeScript buffer at byte-identical offsets,
so the same queries, config and code-health dialects apply. Astro
frontmatter imports and exports become symbols and edges, PascalCase markup
tags become component calls, and pages and endpoints under src/pages are
entry points.
Framework awareness
Repowise recognises common framework conventions and emits edges for them, even when the underlying call goes through a decorator or a runtime registry.
| Language | Frameworks |
|---|---|
| Python | Django (URL patterns, models, signals), FastAPI (route handlers, dependency injection), Flask (route decorators) |
| C# | ASP.NET (controllers, attributes, minimal APIs), EF Core, gRPC-dotnet, dependency injection containers, MassTransit message contracts in C# and VB.NET (publishers and consumers linked across workspace repos) |
| Java / Kotlin | Spring Boot (@RestController, @Component, @Autowired, and every @GetMapping-style route linked to its handler method), Spring Data and MyBatis calls flagged as database I/O, Jakarta / JPA, Quarkus, Micronaut |
| JavaScript / TypeScript | Next.js App Router, Express, NestJS (@Controller, @Module, @Injectable), Hono, Fastify, Remix, SvelteKit, Astro, tRPC, Angular |
| Go | Gin, Echo, Chi router patterns |
| Rust | Axum, Actix routes |
| Ruby | Rails (Zeitwerk autoloading, route DSL) |
| PHP | Laravel routes, TYPO3 conventions |
| Dart | Flutter route tables, runApp() edges, and widget-tree edges from build() bodies |
| GDScript | Godot scenes, autoloads and class_name registration |
Dead-code awareness
The dead-code detector knows about dynamic dispatch patterns that look unused to a static analyzer but aren't:
- Naming patterns:
*Plugin,*Handler,*Adapter,*Middleware - Dynamic imports:
importlib.import_module(),__import__(), dynamicrequire() - Framework convention files (Flask blueprints, FastAPI routers, Django apps, Rails controllers, Laravel providers, TYPO3 extensions)
- Deprecation annotations:
@Deprecated,[Obsolete],@deprecatedand their siblings feed a finding's confidence - Symbols a docs build or an API dump names, and symbols reached by a framework, a container, or a registration decorator
These are excluded from safe_to_delete findings to keep the
false-positive rate near zero.
Code-health marker rollout
Health markers run off a per-language walker map that is independent of
.scm parsing, so a language can parse perfectly for the graph and
still need this map before markers fire. This table is most of the
reason a language is Full rather than Good.
| Language | Complexity / nesting | Class metrics | Assertion smells | Extract Method | Performance risk |
|---|---|---|---|---|---|
| Python | yes | yes | yes | yes | yes |
| TypeScript / JavaScript | yes | yes | yes | yes | yes |
| Svelte · Vue · Astro | yes | yes | yes | yes | yes |
| Java | yes | yes | yes | yes | yes |
| Go | yes | n/a | yes | yes | yes |
| Rust · C++ · C# | yes | yes | yes | yes | yes |
| Scala · Ruby | yes | yes | yes | later | yes |
| Kotlin | yes | yes | yes | blocked | yes |
| Dart | yes | yes | yes | later | yes |
| Object Pascal | yes | n/a | yes | later | yes |
| Razor | yes | n/a | n/a | later | yes |
| PHP | yes | yes | yes | yes | later |
| C | yes | later | later | yes | later |
| Swift | yes | yes | later | later | later |
| F# · Objective-C | yes | yes | later | later | later |
| Shell | yes | n/a | n/a | n/a | n/a |
C, F# and Objective-C reach the complexity walker and carry real cyclomatic complexity and maintainability numbers, without the rest of the marker suite. Swift has a complexity map: function rows (CCN, nesting, cognitive complexity, parameters) and class rows (methods, fields, LCOM4, TCC).
Code Health serves findings on every language. Where a layer measured below the precision bar on a language, its findings carry a Preview label with the latest hand-judged measurement, in the web UI, REST, MCP and the CLI.
Every cell is a deliberate call rather than an oversight: a language
reaches a dialect or it stays silent, and an n/a records a metric the
language cannot carry rather than one nobody got to. Kotlin's Extract
Method is blocked on the grammar, not unscheduled.
Adding a language
Adding new language support is five required steps: a LanguageSpec
module, a manual LanguageTag registration, a tree-sitter .scm query
file, a LanguageConfig entry, and the grammar dependency. Two of those
are one-line registrations, but the LanguageTag edit can't be derived
and has to be made by hand. No changes to parser.py, graph.py, or
any other analysis core file. See
architecture/language-support.md
in the OSS repo for the full recipe, including the optional steps that
buy heritage extraction, named bindings, and call resolution.
Working in a language we don't list? Repowise still indexes the file tree, ownership, and churn; you just won't get import edges or framework-aware analysis. Open a Discussion and we'll prioritise.