Languages

Kotlin

What .kt / .kts add beyond Install, CLI specs, and Graph schema: heuristic vs scip-java, the env flags, and the ids those pages do not spell out. Toolchain fit: cg doctor -h. Why a file stayed heuristic: cg coverage.

Modes

modecallsneeds
heuristic (default)name resolution, labelled heuristictree-sitter (tree-sitter, tree-sitter-kotlin). No JDK or Gradle
exactcompiler-resolved CALLS / INSTANTIATES; declarations and framework facts stay the syntax layerscip-java, opt-in (Exact mode)

--min-confidence resolved (or exact) hides the heuristic call edges. Gradle build/ (including KSP / kapt), .gradle, and IDE folders are skipped. tree-sitter-kotlin 1.1.0 rewrites some sources before parse (same length, so lines and names stay). A file that still errors is split into members (files_reparsed_by_member, members_dropped_by_reparse); dropped members are in cg coverage --details.

rewritewhenstat
suspend blanked, or ; plus spaces when it starts a statementval b = suspend { }, to suspend { }, suspend { }.runCatching(...)suspend_lambdas_rewritten
one leading space → ;get / set call right after a val / var (Ktor routing)accessor_like_calls_rewritten
dynamic parsed as dynamiCa call or function named dynamickeyword_named_calls_rewritten

Exact mode

A scip-java index replaces name-based call edges. Ids stay the syntax layer's, so queries and cg link match both modes. Running scip-java runs the project's Gradle / Maven build, so it is opt-in.

sourcehow
cg index --scip index.scipKotlin documents go to this plugin, not the generic SCIP importer
CG_KOTLIN_SCIP_FILEa prebuilt index
CG_KOTLIN_SCIP=1cg runs scip-java index (cache ~/.cache/cg/scip; CG_NO_CACHE=1 forces a run; CG_INDEXER_TIMEOUT caps it). CG_JAVA_SCIP=1 starts the same run; one project root is indexed once and both plugins read it
scip-javaKotlin (Gradle builds checked)install
0.12.xup to 2.1cs install scip-java
0.13.x2.2.0–2.2.10launcher; CG_SCIP_JAVA is one path or several joined by :
none2.2.20 and newerstays heuristic

cg reads the version the build declares, tries the matching release first, then the next if the compiler plugin does not load. Index stats record kotlin_version, indexer, and failed attempts. cg doctor <project> names the release that fits. install.sh --with kotlin installs the supported pair (Windows: cg doctor).

Java in that index becomes java nodes with the Java plugin's ids (method:pkg.Foo.bar, not method:pkg.Foo::bar), including fields, and exact Kotlin ↔ Java and Java → Java calls, including AppKt.build(), a @file:JvmName facade, Foo.Companion.x(), @JvmStatic, and getX / isX / setX on a Kotlin property. The two layers do not create a second node for the same Java symbol. Without the index, .java files are indexed by the Java plugin (heuristic), and Kotlin → Java calls resolve through imports and the same package; Java → Kotlin calls resolve FooKt.bar(), Foo.Companion.x(), @JvmStatic, and getX / setX / isX (Java interop). A --scip file with Java documents is consumed by the Java plugin. Go and other languages still use the generic importer. Spring facts for both languages are extracted by the shared JVM module cg_code_graph/plugins/jvm/spring.py (Java). Stats exact_vs_heuristic is precision (heuristic call edges the compiler confirms) and recall (compiler edges the heuristic layer had found). Property read / write edges stay and are left out of that pair; a property read is not a call in the SCIP index.

still heuristicwhy
Android modulesthe build needs the SDK, and scip-java does not compile compileDebugKotlin (android_modules in the stats; named in the coverage reason)
FAIL_ON_PROJECT_REPOSAndroid template default rejects the repository the scip-java Gradle plugin adds
files the index omitsthose files keep heuristic calls, counted in the reason

Query specs

Class.method and Class::method follow query targets. Kotlin-only shapes:

spec or idselects
method:<Type>.<name>, function:<pkg>.<name>a member, or a top-level / extension function (receiver on an extension)
function:pkg.name@androidone platform's actual (IMPLEMENTED_BY from expect). Source sets (androidMain, iosMain, iosTest, …) carry platform tags — cg platforms, --platform
page:kotlin:orders/{id}Compose Navigation, typed routes, or Navigation 3 entry. A nested destination keeps its outer class (page:kotlin:SettingsRoute.Standard); a wrapper of composable<T> still yields a page (attrs.wrapper)
field:<Type>.<name>a stored val / var (constructor property included): READS_PROP / WRITES_PROP for x, this.x, and v.x when v's type is known. cg readers Type.prop / cg writers Type.prop
enum_case:<Enum>.NAME, constant:<Type>.NAMEUSES_VALUE when the spelling is certain (Color.RED, a companion's K.A). An object's vals stay constants
route:GET /a/{id}Ktor routing / fun Route.x(), or a Spring mapping
http:METHOD pathRetrofit (@GET / @HTTP plus baseUrl), Ktor client, or OkHttp. "$base/x" keeps {base} so cg link can match the path

A custom get / set, by lazy, or other delegate is a method: / function: node (kotlin_kind: property). Reads and writes are CALLS (property: read / write / read_write); calls inside the accessor carry accessor. impact prints a callable reference as (ref: callback) (REFERENCES_FN, how: callback, both modes), an accessor body as (get) / (set) / (lazy) / (delegate), and a stored-property initializer as (in a property). Two to five methods of one name are candidate edges (binding: "candidate"): flagged by cg tests / impact, left out of platform divergence. A library-typed receiver (Headers.build { }, client: HttpClient) is not bound by name.

Framework facts

areawhat you can query
Ktoreach handler lambda is its own node; authenticate("jwt") is the route's guard; get<Articles.Id> takes the path from @Resource and its parent
Springsame facts as Java, from cg_code_graph/plugins/jvm/spring.py (Java): class @RequestMapping prefixes; @PreAuthorize / @Secured / @RolesAllowed as guards; the first SecurityFilterChain whose securityMatcher matches (by @Order, then source order; a chain with none matches every route; a RequestMatcher bean is skipped, security_chains_unknown_matcher); @Scheduled and @KafkaListener / @RabbitListener / @JmsListener / @SqsListener / @EventListener entry points
TablesSpring Data calls on a repository receiver (JpaRepository, CrudRepository, coroutine / reactive / Mongo) and Exposed selectAll / insert / update / deleteWhere → READS_TABLE / WRITES_TABLE. Table name: @Table or Spring Boot snake_case; Exposed uses the name argument, or the object name without Table
Androidmanifest activities (ui_page), services / receivers / providers (listener), deep links, Worker / CoroutineWorker / JobService (queue_job); navigate(...) → NAVIGATES_TO
Testssrc/test, src/androidTest, *Test source sets, *Test.kt. Framework from imports: junit5, junit4, kotlin-test, testng, kotest. androidTest counts as UI; a path through an *Activity is omitted unless --through-roots
cg index examples/bookstore-django  --db /tmp/dj.db
cg index examples/bookstore-android --db /tmp/android.db
cg link --backend /tmp/dj.db --frontend /tmp/android.db --db /tmp/android-link.db
cg path "page:kotlin:checkout/{bookId}" "table:catalog_order" --db /tmp/android-link.db