Migrate to Exposed 1.0
Automates the mechanical parts of migrating a Kotlin project from Exposed 0.61.0 to Exposed 1.0. Anything that requires human judgment is detected and reported in a summary at the end so the user can address it manually using the migration guide.
Supported starting version: 0.61.0 (the last pre-1.0 release). The official Migration-Guide-1-0-0.md was written specifically for the 0.61.0 → 1.0.0 jump. If the detected version is older (e.g. 0.41.x, 0.55.x), the skill warns and asks for explicit confirmation before proceeding: the rules will still rewrite the package paths it recognises, but APIs introduced between the detected version and 0.61.0 are out of scope, and the user should upgrade to 0.61.0 first or expect a noisier manual-review list.
The canonical source for what to migrate is the official guide:
https://www.jetbrains.com/help/exposed/migration-guide-1-0-0.html
(or the local Migration-Guide-1-0-0.md if available).
The reference files in ./references/ encode that guide as machine-applicable rules:
package-renames.md— every import-level rename, organised by category (always-apply, JDBC-conditional, SqlExpressionBuilder, UUID, datetime, addLogger).module-detection.md— heuristics for the project fact table (usesJdbc,usesR2dbc,usesDao, …).build-files.md— Gradle KTS / Groovy / version-catalog / Maven edit patterns.manual-review.md— cases the skill must NOT auto-apply, with detection patterns.
Load each reference file only when the corresponding phase below needs it.
Operating principles
- Operate on the user's current working tree. Do not create branches or commits.
- Never write outside the user's project root.
- Never run
./gradlewor any other build command on the user's project. - Idempotent at line level: every edit is a precise
Edit(old_string/new_string), so re-running the skill after partial completion is safe. - Cross-check: if a file was changed externally between Phase 1 and Phase 3 (an
Editfails becauseold_stringdoesn't match), skip the file with a "skipped — file changed" reason and continue.
Phase 1 — Discover (read-only)
Determine whether migration is needed.
The real signal is whether any source file uses the old
0.xpackage paths (org.jetbrains.exposed.sql.*,org.jetbrains.exposed.dao.*,org.jetbrains.exposed.r2dbc.sql.*, …). Version coordinates in build files are a useful hint but don't always reflect reality — projects on a1.0.0-beta-N/1.0.0-rc-Nrelease already use thev1.*namespace and don't need this skill, even though their coordinate isn't strictly0.x.a. Source scan: look for any
import org.jetbrains.exposed.sql.orimport org.jetbrains.exposed.dao.(and the R2DBC equivalents) in any.ktfile undersrc/. Record file:line for use in the rename-scan step below.b. Build-file scan: look for
org.jetbrains.exposed:coordinates ingradle/libs.versions.toml, everybuild.gradle.kts/build.gradle, andpom.xml. Record every coordinate version found.c. If (a) finds zero old-namespace imports AND (b) finds no pre-1.0 coordinate (everything is either
1.0.0proper or higher, or no Exposed coordinate at all), stop here and report:No migration needed. Your project does not use the old `org.jetbrains.exposed.sql.*` / `.dao.*` namespace and the build files do not pin a pre-1.0 release. Projects on `1.0.0-beta-N` / `1.0.0-rc-N` already use the new `v1.*` namespace and don't need this skill.d. Otherwise, migration applies. Proceed with the version check below.
Version check. The supported starting version is
0.61.0. If the detected coordinate is something else under0.x(e.g.0.41.x,0.55.x), print the warning below and let the user decide whether to continue:⚠️ Detected Exposed <X.Y.Z>, but this skill targets the 0.61.0 → 1.0.0 migration. Older 0.x versions may use APIs that were renamed or removed before 0.61.0; those changes are NOT covered by this skill. Recommended path: 1. Upgrade your project to Exposed 0.61.0 first. 2. Re-run this skill. Proceed anyway? The skill will rewrite recognised package paths, but expect a noisier "manual review" list and possibly missed changes.If the user declines, exit cleanly. If they confirm, continue but include the version-mismatch warning in the Phase 4 summary.
Beta/RC of 1.0 with
sql.*imports somehow still present (e.g. a half-finished manual migration) is also valid input: the source scan in (a) catches it and the skill proceeds without emitting the0.61.0warning, since the user is already targeting 1.0.0.Build the project fact table per
references/module-detection.md.Enumerate every Kotlin file under
src/(recursively). For each file, run two scans:- Rename scan — grep for any pattern in
references/package-renames.md(sections 1–6). Record file:line + which pattern hit. - Manual-review scan — grep for any pattern in
references/manual-review.md. Record file:line + which case.
- Rename scan — grep for any pattern in
Print a Phase 1 summary to the user:
Discovered: • Exposed version <X.Y.Z> in <build files> • Project flavor: usesJdbc=<bool>, usesR2dbc=<bool>, usesDao=<bool>, ... • <N> Kotlin files contain 0.x imports (<M> imports total) • <K> manual-review cases across <L> files • Build files to update: <list>
Phase 2 — Plan (interactive checkpoint)
Print a one-paragraph plan to the user:
Planned changes: 1. Apply <category-1> rewrites to <N1> files. 2. Apply <category-2> rewrites to <N2> files. 3. Update <build-file> dependencies. 4. Report <K> manual-review items at the end (no source edits).Stop and ask the user to confirm before any edits. Wait for their response.
If the user says "skip ", remove that category from the plan and proceed. If the user declines, exit cleanly with no changes.
Phase 3 — Apply (write phase)
For each Kotlin source file with rename hits, apply edits in this order:
a. Section 1 (always-apply renames) from references/package-renames.md. Each row
becomes one or more Edit calls (old_string = the 0.61.0 FQN; new_string = the
1.0.0 FQN).
b. Section 2 (JDBC-conditional moves) — only if the project's usesJdbc is true AND
the file does not contain any R2DBC import. Apply each row as in (a).
c. Section 3 (SqlExpressionBuilder lambda imports) — for every line of the form
import org.jetbrains.exposed.sql.SqlExpressionBuilder.<NAME>, rewrite to
import org.jetbrains.exposed.v1.core.<NAME>. The list of <NAME> values is in
package-renames.md Section 3, but the rule is independent of which method —
if the prefix matches, rewrite.
d. Section 4 (UUID), Section 5 (Datetime), Section 6 (StdOutSqlLogger / addLogger)
from package-renames.md — apply each row as in (a). For
import org.jetbrains.exposed.sql.addLogger, delete the import line entirely
instead of rewriting it (the function became a method).
If any Edit fails (old_string not unique or not present), skip just that edit, record
the reason, and continue with the rest of the file.
For build files (build.gradle.kts, build.gradle, gradle/libs.versions.toml,
pom.xml):
e. Apply the patterns in references/build-files.md for the matching flavor. For
ambiguous lines (a custom version variable that does not match the
exposed-name rule), append // TODO: review for Exposed 1.0 (or # TODO: …
for .toml, or <!-- TODO: ... --> for XML) and add the line to the manual-review
summary.
The skill never modifies files matched by manual-review.md patterns beyond the
mechanical rename edits in (a)–(d). The matches themselves get reported in Phase 4.
Phase 4 — Summarise
Print a markdown report to stdout:
# Migration summary
## ✅ Files modified (<N> total)
### Package renames (Section 1)
- src/main/kotlin/com/example/Foo.kt — 4 imports
- src/main/kotlin/com/example/Bar.kt — 2 imports
...
### JDBC moves (Section 2)
- src/main/kotlin/com/example/Foo.kt — 3 imports
...
### SqlExpressionBuilder rewrites (Section 3)
- src/main/kotlin/com/example/Foo.kt — 1 import
...
### UUID / Datetime / Logger
- src/main/kotlin/com/example/Models.kt — 1 import
## ✅ Build files modified
```diff
--- build.gradle.kts
+++ build.gradle.kts
@@
-implementation("org.jetbrains.exposed:exposed-core:0.61.0")
+implementation("org.jetbrains.exposed:exposed-core:1.0.0")
+implementation("org.jetbrains.exposed:exposed-jdbc:1.0.0")
```
## ⚠️ Manual review needed (<K> items)
⚠️ src/main/kotlin/com/example/Tx.kt:42 custom-transaction-extension
detected: fun Transaction.getVersion()
guide: "Transactions — Custom functions"
action: change the receiver to `JdbcTransaction`.
⚠️ src/main/kotlin/com/example/Tx.kt:88 transaction-signature
detected: transaction(db.transactionManager.defaultIsolationLevel, db = db) { ... }
guide: "transaction() signature changed"
action: db is now the first positional parameter; use named args for the rest.
...
## ⏭️ Skipped files (<S> total)
- src/main/kotlin/com/example/Old.kt — old_string did not match (file may have been edited)
## 📋 Suggested next steps
1. Review the diff: `git diff`
2. Compile: `./gradlew compileKotlin` (or your project's build command)
3. Run tests: `./gradlew test`
4. Address each ⚠️ item using the official migration guide:
https://www.jetbrains.com/help/exposed/migration-guide-1-0-0.html
5. When everything compiles and tests pass, commit:
`git commit -am "Migrate to Exposed 1.0"`
The skill stops here. No further actions, no commits.
Edge cases
- Project uses both JDBC and R2DBC. The per-file decision in Phase 3 (b) handles this: files importing R2DBC are not given JDBC-conditional moves.
exposed-spring-boot-starter. The skill does NOT replace it withexposed-spring-boot4-starter. That's a Spring upgrade the user opts into. A note is added to the manual-review summary instead.- Custom Exposed-version variable. If named to contain
exposed(case-insensitive), bumped automatically. Otherwise reported for manual review. - No 0.x usage. Exit cleanly in Phase 1 with "no migration needed."
- Nothing left to do after rerun. All
Editcalls fail becauseold_stringis already the new value. The summary reports zero modifications and zero skips.
Source: JetBrains/Exposed — distributed by TomeVault.