Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,20 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### New Features

- Jetpack Compose functions are now indexed as UI components: any Kotlin function or method annotated `@Composable` is recorded with the `component` kind, so `codegraph_search` with `kind=component` finds your Compose UI the same way it already finds React components.

- Kotlin annotations are now recorded on the symbol they annotate and shown by `codegraph_node`, so your agent can see that a class is `@HiltViewModel` or `@Entity`, or that a function is `@Composable`, without opening the file. Framework annotations come from libraries outside your project, so previously they left no trace on the graph at all.

### Fixes

- Kotlin annotations written with arguments (`@Preview(showBackground = true)`, `@Entity(tableName = "users")`) were ignored entirely — only bare annotations like `@Override` were picked up. Both forms are now captured, as is Kotlin's bracket form (`@[Suppress("x") JvmStatic]`).

- Annotations on Kotlin interfaces and enums were dropped, so a Room `@Dao` interface or a `@Serializable` enum showed nothing. Both are now recorded.

- Kotlin functions referenced as values (`register(::Header)`) kept their reference edge when the target is a Compose composable, instead of dropping it.


## [1.6.0] - 2026-08-26

Expand Down
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ Follow [@getcodegraph](https://x.com/getcodegraph) on X for updates.
- [Key Features](#key-features)
- [Framework-aware Routes](#framework-aware-routes)
- [Mixed iOS / React Native / Expo bridging](#mixed-ios--react-native--expo-bridging)
- [Android: Jetpack Compose, Hilt, and Room](#android-jetpack-compose-hilt-and-room)
- [Quick Start](#quick-start)
- [How It Works](#how-it-works)
- [CLI Reference](#cli-reference)
Expand Down Expand Up @@ -367,6 +368,24 @@ Each bridge emits edges tagged `provenance:'heuristic'` with `metadata.synthesiz

---

## Android: Jetpack Compose, Hilt, and Room

Modern Android leans on annotations for structure, and those annotations come from libraries that aren't part of your project — so on their own they leave nothing behind to search. CodeGraph records them on the symbol they annotate:

- **`@Composable` functions are indexed as UI components**, so `codegraph_search` with `kind=component` finds your Compose UI the same way it finds React components.
- **Annotations are shown by `codegraph_node`**, so your agent can see that a class is `@HiltViewModel` or `@Entity`, or a function is `@Composable`, without opening the file.

**Measured on real Android codebases:**

| Repo | Kotlin files | Compose components | Annotated symbols | Most common annotations |
|---|---|---|---|---|
| [Now in Android](https://github.com/android/nowinandroid) (Compose + Room + Hilt) | 310 | 151 | 617 | `@Composable` 151 · `@Binds` 27 · `@Provides` 23 · `@Module` 22 |
| [compose-samples](https://github.com/android/compose-samples) | 355 | 545 | 750 | `@Composable` 545 · `@Preview` 89 · `@Query` 19 |

Annotations written with arguments — `@Preview(showBackground = true)`, `@Entity(tableName = "users")`, `@Query("SELECT …")` — used to be skipped entirely, as were annotations on interfaces and enums, which is where Room puts `@Dao`. All of them are now captured, which is most of what Room and Hilt are made of. On non-Compose Kotlin libraries the same fix recovers annotation coverage without inventing components: [OkHttp](https://github.com/square/okhttp) picks up 3,895 annotated symbols and [okio](https://github.com/square/okio) 1,957, with zero component nodes, because neither uses Compose.

---

## Quick Start

### 1. Run the Installer
Expand Down
47 changes: 47 additions & 0 deletions __tests__/fixtures/kernel-parity/torture.kt
Original file line number Diff line number Diff line change
Expand Up @@ -265,3 +265,50 @@ fun labeledLambda() {
}

fun whereClause(): Int where Int : Comparable<Int> = 1

// --- annotation extraction (@Composable component classification) -------------
// Both arms must agree on: the `component` kind, the persisted `decorators`
// list AND its order, and the decorates refs. The arg-bearing form used to be
// dropped entirely (constructor_invocation was not unwrapped).
@Composable
fun AnnoComposable() {
AnnoQualified()
}

@Preview(showBackground = true, name = "dark")
@Composable
fun AnnoPreviewComposable() {}

@androidx.compose.runtime.Composable
fun AnnoQualified() {}

@[Suppress("unused") JvmStatic]
fun annoBracketed() {}

@Deprecated("gone", ReplaceWith("annoBracketed"))
fun annoNestedArgs() {}

@receiver:Fancy
fun String.annoUseSite(): String = this.uppercase()

@HiltViewModel
class AnnoAnnotatedClass {
@Composable
fun AnnoMember() {}
}

fun annoHolder(content: @Composable () -> Unit) {
content()
}

@JvmStatic
expect fun annoExpectPlatform(): String

@Dao
interface AnnoDao {
@Query("SELECT * FROM t")
fun annoGetAll(): List<String>
}

@Serializable
enum class AnnoSyncKind { FULL, DELTA }
3 changes: 2 additions & 1 deletion __tests__/kernel-kotlin-parity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
* fallback, expect/actual → node DECORATORS (the KMP synthesizer feed),
* the bodiless-vs-bodied class header asymmetry, comment-glued
* import/package extents, KDoc dropped-and-chain-breaking docstrings,
* `@Marker` decorates vs `@Anno(args)` nothing, zero type-annotation refs,
* `@Marker` and `@Anno(args)` both emitting decorates and a persisted node
* decorator name, zero type-annotation refs,
* zero instantiates, the #750 capitalized-chain re-encode, paren-then-
* lambda garbage callees, `${X}`-reads-vs-`$X`-non-reads value refs and the
* packaged-file target drop) plus a `.kts` script fixture (file-attributed
Expand Down
Loading