diff --git a/CHANGELOG.md b/CHANGELOG.md
index f1dcb234..638f14f7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
## [Unreleased]
+### Added
+
+- Add support for reading ktlint version from `ktlint-plugins.properties` file [#1001](https://github.com/JLLeitschuh/ktlint-gradle/pull/1001)
+ - Enables automatic synchronization of ktlint version between IntelliJ plugin and Gradle builds
+ - When `ktlint-plugins.properties` exists in project root with `ktlint-version` property, that version is used as default
+ - Explicit version configuration in build script still takes precedence
+
## [14.0.1] - 2025-11-10
- Update build to work with gradle 9.1 and Java 25 [#962](https://github.com/JLLeitschuh/ktlint-gradle/pull/962)
diff --git a/README.md b/README.md
index 29cc993a..21496e85 100644
--- a/README.md
+++ b/README.md
@@ -279,6 +279,44 @@ ktlint {
```
+#### KtLint IntelliJ Plugin Integration
+
+If you're using the [ktlint IntelliJ plugin](https://github.com/nbadal/ktlint-intellij-plugin), you can automatically synchronize the ktlint version between your IDE and Gradle builds by creating a `ktlint-plugins.properties` file in your project root directory.
+
+The IntelliJ plugin can read and write the ktlint version to this file. When this file exists and contains a `ktlint-version` property, the Gradle plugin will automatically use that version as the default.
+
+Example `ktlint-plugins.properties` file:
+```properties
+ktlint-version=1.2.1
+```
+
+**Key benefits:**
+- Ensures consistency between IDE and build tool ktlint versions
+- Simplifies version management across your development team
+- Allows IntelliJ plugin to manage the version
+
+**Override behavior:**
+Even when the `ktlint-plugins.properties` file exists, you can still explicitly set the version in your `build.gradle` or `build.gradle.kts` file, which will take precedence:
+
+
+Groovy
+
+```groovy
+ktlint {
+ version = "1.3.0" // This overrides the version in ktlint-plugins.properties
+}
+```
+
+
+Kotlin
+
+```kotlin
+ktlint {
+ version.set("1.3.0") // This overrides the version in ktlint-plugins.properties
+}
+```
+
+
### Configuration
The following configuration block is _optional_.
diff --git a/plugin/src/main/kotlin/org/jlleitschuh/gradle/ktlint/KtlintExtension.kt b/plugin/src/main/kotlin/org/jlleitschuh/gradle/ktlint/KtlintExtension.kt
index 7bdee57e..217a69f2 100644
--- a/plugin/src/main/kotlin/org/jlleitschuh/gradle/ktlint/KtlintExtension.kt
+++ b/plugin/src/main/kotlin/org/jlleitschuh/gradle/ktlint/KtlintExtension.kt
@@ -31,8 +31,19 @@ open class KtlintExtension @Inject internal constructor(
/**
* The version of KtLint to use.
+ *
+ * If a `ktlint-plugins.properties` file exists in the project root with a `ktlint-version` property,
+ * that version will be used as the default. Otherwise, defaults to "1.5.0".
+ *
+ * This property can be explicitly set in the build script to override the default behavior.
*/
- val version: Property = objectFactory.property { set("1.5.0") }
+ val version: Property = objectFactory.property {
+ // Try to read version from ktlint-plugins.properties file first
+ val versionFromFile = readKtlintVersionFromPropertiesFile(
+ projectLayout.projectDirectory.asFile.toPath()
+ )
+ set(versionFromFile ?: "1.5.0")
+ }
/**
* Enable relative paths in reports
diff --git a/plugin/src/main/kotlin/org/jlleitschuh/gradle/ktlint/PluginUtil.kt b/plugin/src/main/kotlin/org/jlleitschuh/gradle/ktlint/PluginUtil.kt
index c9d7dd8e..b8edc230 100644
--- a/plugin/src/main/kotlin/org/jlleitschuh/gradle/ktlint/PluginUtil.kt
+++ b/plugin/src/main/kotlin/org/jlleitschuh/gradle/ktlint/PluginUtil.kt
@@ -33,6 +33,28 @@ internal inline fun Project.registerTask(
}
internal const val EDITOR_CONFIG_FILE_NAME = ".editorconfig"
+internal const val KTLINT_PLUGINS_PROPERTIES_FILE_NAME = "ktlint-plugins.properties"
+internal const val KTLINT_PLUGINS_VERSION_PROPERTY = "ktlint-version"
+
+/**
+ * Reads the ktlint version from the ktlint-plugins.properties file if it exists.
+ * This file is used by the ktlint IntelliJ plugin to store the ktlint version.
+ *
+ * @param projectDir The project directory to search for the properties file
+ * @return The ktlint version string if found, null otherwise
+ */
+internal fun readKtlintVersionFromPropertiesFile(projectDir: Path): String? {
+ val propertiesFile = projectDir.resolve(KTLINT_PLUGINS_PROPERTIES_FILE_NAME)
+ if (!Files.exists(propertiesFile) || !Files.isReadable(propertiesFile)) {
+ return null
+ }
+
+ return propertiesFile.toFile().inputStream().use { input ->
+ val properties = java.util.Properties()
+ properties.load(input)
+ properties.getProperty(KTLINT_PLUGINS_VERSION_PROPERTY)?.takeIf { it.isNotBlank() }
+ }
+}
internal fun getEditorConfigFiles(currentProjectDir: Path): Set {
val result = mutableSetOf()
diff --git a/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/KtlintPluginsPropertiesTest.kt b/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/KtlintPluginsPropertiesTest.kt
new file mode 100644
index 00000000..7f5c42e1
--- /dev/null
+++ b/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/KtlintPluginsPropertiesTest.kt
@@ -0,0 +1,149 @@
+package org.jlleitschuh.gradle.ktlint
+
+import org.assertj.core.api.Assertions.assertThat
+import org.gradle.util.GradleVersion
+import org.jlleitschuh.gradle.ktlint.testdsl.CommonTest
+import org.jlleitschuh.gradle.ktlint.testdsl.GradleTestVersions
+import org.jlleitschuh.gradle.ktlint.testdsl.build
+import org.jlleitschuh.gradle.ktlint.testdsl.project
+
+@GradleTestVersions
+class KtlintPluginsPropertiesTest : AbstractPluginTest() {
+
+ @CommonTest
+ fun shouldUseVersionFromKtlintPluginsPropertiesFileWhenPresent(gradleVersion: GradleVersion) {
+ project(gradleVersion) {
+ // Create ktlint-plugins.properties file with version
+ projectPath.resolve(KTLINT_PLUGINS_PROPERTIES_FILE_NAME).writeText(
+ """
+ ktlint-version=1.2.1
+ """.trimIndent()
+ )
+
+ withCleanSources()
+
+ buildGradle.appendText(
+ """
+ tasks.register("printKtlintVersion") {
+ doLast {
+ println("Ktlint version: " + ktlint.version.get())
+ }
+ }
+ """.trimIndent()
+ )
+
+ build("printKtlintVersion") {
+ assertThat(output).contains("Ktlint version: 1.2.1")
+ }
+ }
+ }
+
+ @CommonTest
+ fun shouldUseDefaultVersionWhenKtlintPluginsPropertiesFileIsAbsent(gradleVersion: GradleVersion) {
+ project(gradleVersion) {
+ withCleanSources()
+
+ buildGradle.appendText(
+ """
+ tasks.register("printKtlintVersion") {
+ doLast {
+ println("Ktlint version: " + ktlint.version.get())
+ }
+ }
+ """.trimIndent()
+ )
+
+ build("printKtlintVersion") {
+ assertThat(output).contains("Ktlint version: 1.5.0")
+ }
+ }
+ }
+
+ @CommonTest
+ fun shouldAllowExplicitVersionOverrideEvenWhenPropertiesFileExists(gradleVersion: GradleVersion) {
+ project(gradleVersion) {
+ // Create ktlint-plugins.properties file with version
+ projectPath.resolve(KTLINT_PLUGINS_PROPERTIES_FILE_NAME).writeText(
+ """
+ ktlint-version=1.2.1
+ """.trimIndent()
+ )
+
+ withCleanSources()
+
+ buildGradle.appendText(
+ """
+ ktlint {
+ version = "1.3.0"
+ }
+
+ tasks.register("printKtlintVersion") {
+ doLast {
+ println("Ktlint version: " + ktlint.version.get())
+ }
+ }
+ """.trimIndent()
+ )
+
+ build("printKtlintVersion") {
+ assertThat(output).contains("Ktlint version: 1.3.0")
+ }
+ }
+ }
+
+ @CommonTest
+ fun shouldUseDefaultVersionWhenKtlintVersionPropertyIsBlank(gradleVersion: GradleVersion) {
+ project(gradleVersion) {
+ // Create ktlint-plugins.properties file with blank version
+ projectPath.resolve(KTLINT_PLUGINS_PROPERTIES_FILE_NAME).writeText(
+ """
+ ktlint-version=
+ """.trimIndent()
+ )
+
+ withCleanSources()
+
+ buildGradle.appendText(
+ """
+ tasks.register("printKtlintVersion") {
+ doLast {
+ println("Ktlint version: " + ktlint.version.get())
+ }
+ }
+ """.trimIndent()
+ )
+
+ build("printKtlintVersion") {
+ assertThat(output).contains("Ktlint version: 1.5.0")
+ }
+ }
+ }
+
+ @CommonTest
+ fun shouldUseDefaultVersionWhenPropertiesFileHasNoKtlintVersionProperty(gradleVersion: GradleVersion) {
+ project(gradleVersion) {
+ // Create ktlint-plugins.properties file without ktlint-version
+ projectPath.resolve(KTLINT_PLUGINS_PROPERTIES_FILE_NAME).writeText(
+ """
+ some-other-property=value
+ """.trimIndent()
+ )
+
+ withCleanSources()
+
+ buildGradle.appendText(
+ """
+ tasks.register("printKtlintVersion") {
+ doLast {
+ println("Ktlint version: " + ktlint.version.get())
+ }
+ }
+ """.trimIndent()
+ )
+
+ build("printKtlintVersion") {
+ assertThat(output).contains("Ktlint version: 1.5.0")
+ }
+ }
+ }
+}
diff --git a/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/PluginUtilTest.kt b/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/PluginUtilTest.kt
index 43ef281b..4febaa90 100644
--- a/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/PluginUtilTest.kt
+++ b/plugin/src/test/kotlin/org/jlleitschuh/gradle/ktlint/PluginUtilTest.kt
@@ -40,4 +40,99 @@ internal class PluginUtilTest {
delete()
}
}
+
+ @Test
+ fun `test readKtlintVersionFromPropertiesFile returns version when file exists`() {
+ temporaryFolder.resolve(KTLINT_PLUGINS_PROPERTIES_FILE_NAME).apply {
+ createNewFile()
+ writeText(
+ """
+ ktlint-version=1.2.1
+ """.trimIndent()
+ )
+ val version = readKtlintVersionFromPropertiesFile(temporaryFolder.toPath())
+ Assertions.assertEquals("1.2.1", version) {
+ "correctly reads version from properties file"
+ }
+ delete()
+ }
+ }
+
+ @Test
+ fun `test readKtlintVersionFromPropertiesFile returns null when file does not exist`() {
+ val version = readKtlintVersionFromPropertiesFile(temporaryFolder.toPath())
+ Assertions.assertNull(version) {
+ "returns null when properties file does not exist"
+ }
+ }
+
+ @Test
+ fun `test readKtlintVersionFromPropertiesFile returns null when version property is missing`() {
+ temporaryFolder.resolve(KTLINT_PLUGINS_PROPERTIES_FILE_NAME).apply {
+ createNewFile()
+ writeText(
+ """
+ some-other-property=value
+ """.trimIndent()
+ )
+ val version = readKtlintVersionFromPropertiesFile(temporaryFolder.toPath())
+ Assertions.assertNull(version) {
+ "returns null when ktlint-version property is missing"
+ }
+ delete()
+ }
+ }
+
+ @Test
+ fun `test readKtlintVersionFromPropertiesFile handles whitespace correctly`() {
+ temporaryFolder.resolve(KTLINT_PLUGINS_PROPERTIES_FILE_NAME).apply {
+ createNewFile()
+ writeText(
+ """
+ ktlint-version = 1.3.0
+ """.trimIndent()
+ )
+ val version = readKtlintVersionFromPropertiesFile(temporaryFolder.toPath())
+ Assertions.assertEquals("1.3.0", version) {
+ "correctly handles whitespace around equals sign"
+ }
+ delete()
+ }
+ }
+
+ @Test
+ fun `test readKtlintVersionFromPropertiesFile returns null when version is blank`() {
+ temporaryFolder.resolve(KTLINT_PLUGINS_PROPERTIES_FILE_NAME).apply {
+ createNewFile()
+ writeText(
+ """
+ ktlint-version=
+ """.trimIndent()
+ )
+ val version = readKtlintVersionFromPropertiesFile(temporaryFolder.toPath())
+ Assertions.assertNull(version) {
+ "returns null when ktlint-version is blank"
+ }
+ delete()
+ }
+ }
+
+ @Test
+ fun `test readKtlintVersionFromPropertiesFile handles multiple properties`() {
+ temporaryFolder.resolve(KTLINT_PLUGINS_PROPERTIES_FILE_NAME).apply {
+ createNewFile()
+ writeText(
+ """
+ some-property=value1
+ ktlint-version=1.4.0
+ another-property=value2
+ """.trimIndent()
+ )
+ val version = readKtlintVersionFromPropertiesFile(temporaryFolder.toPath())
+ Assertions.assertEquals("1.4.0", version) {
+ "correctly finds ktlint-version among multiple properties"
+ }
+ delete()
+ }
+ }
}