Teaches AI agents to write idiomatic Kotlin instead of Java-in-a-.kt-file.
98
98%
Does it follow best practices?
Impact
99%
1.20xAverage score across 8 eval scenarios
Passed
No known issues
Teaches AI coding agents to write idiomatic Kotlin instead of Java-in-a-.kt-file. Six rules covering the highest-leverage Kotlin idioms, three skills that perform the common conversions, and a CI script that gates the JUnit-to-Kotest migration deterministically.
A coding agent without Kotlin context writes Kotlin like a Java developer who learned the syntax over a long weekend: regular class instead of data class, manual equals / hashCode, var properties everywhere, Optional<String> for nullables, JUnit assertions in test files. It compiles. It runs. It's Java in a .kt file. This plugin closes the gap — one tessl install and your agent knows the difference.
tessl install jbaruch/kotlin-tutor| ID | Rule | Summary |
|---|---|---|
| K-1 | prefer-val | Properties default to val; var is opt-in for mutation |
| K-2 | nullable-question-mark | String? not Optional<String>; lean on ?. and ?: |
| K-3 | use-data-class | Value types are data class; never hand-roll equals/hashCode/toString |
| K-4 | kotest-over-junit | Tests use Kotest matchers and specs, not assertEquals and @Test |
| K-5 | prefer-stdlib-scope | Use .let / .also / .apply over imperative Java patterns |
| K-6 | extension-over-util | Extension functions, not *Utils classes with static methods |
| Skill | Description |
|---|---|
| kotlinify-tests | Convert JUnit-style test classes to idiomatic Kotest specs; delegates to verify-no-junit-assertions.sh for the deterministic gate |
| pojoify-to-dataclass | Refactor a Java-style POJO (manual equals/hashCode/toString, bean accessors) into an idiomatic Kotlin data class |
| nullable-cleanup | Replace Optional<T> with T? and the ?. / ?: / ?.let { } operators |
| Script | Purpose |
|---|---|
| verify-no-junit-assertions.sh | Fails CI if any test file under src/test/ still uses JUnit-style assertions. Delegated from the kotlinify-tests skill; runs standalone as a CI gate. Enforces rule K-4. |
| Scenario | Targets skill | Purpose |
|---|---|---|
| scenario-0, scenario-1, scenario-2 | kotlinify-tests | Real evals (auto-generated, curated to remove bleeding) |
| scenario-pojoify-subscription | pojoify-to-dataclass | Java-style POJO → idiomatic data class refactor |
| scenario-nullable-user-service | nullable-cleanup | Optional<T> API → Kotlin nullable types, caller updated alongside |
| trial-1-task-leaks-language | — | Pedagogical — slide-59 demo, task leaks language, baseline 100% |
| trial-2-rubric-grades-features | — | Pedagogical — slide-59 demo, rubric still grades features only, baseline still 100% |
| trial-3-rubric-weights-language | — | Pedagogical — slide-59 demo, rubric weights idiomatic Kotlin 80%, lift becomes measurable |
The pedagogical scenarios are intentionally bad eval design — they ship publicly so audiences of the talk "You're Absolutely Right (and Other Lies My AI Told Me)" can browse them in the registry and see why the failures land at 100% / 100% / ~20%-then-~95%.
The kotlinify-tests skill demonstrates the script-delegation pattern in miniature: the skill does the judgment work (which Kotest matcher fits this JUnit call? what spec style preserves the original structure?), and verify-no-junit-assertions.sh does the deterministic gate (did any JUnit assertion slip through?). Judgment lives in the skill; mechanism lives in the shell. The script also runs when the agent isn't present — human commits go through the same gate in CI.
alwaysApply: true); agents see them on every promptdescription fieldK-1 through K-6) are talk-shorthand for the descriptive filenames; the registry uses the filenamesSee CHANGELOG.md for version history.