@file:OptIn(ExperimentalKotlinGradlePluginApi::class)
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
import org.gradle.api.DefaultTask
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import java.io.File
plugins {
kotlin("multiplatform")
id("org.jetbrains.compose")
kotlin("plugin.serialization")
id("org.jetbrains.kotlin.plugin.compose")
id("com.android.application")
id("app.cash.sqldelight") version "2.1.0"
id("de.comahe.i18n4k") version "0.11.1"
alias(libs.plugins.ktlint)
`maven-publish`
}
repositories {
mavenCentral()
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
google()
maven("https://oss.sonatype.org/content/repositories/snapshots/")
maven("https://packages.jetbrains.team/maven/p/ij/intellij-dependencies/")
maven("https://jogamp.org/deployment/maven")
}
sqldelight {
databases {
create("DevInsDatabase") {
packageName.set("cc.unitmesh.devins.db")
// generateAsync = true
}
}
}
i18n4k {
sourceCodeLocales = listOf("en", "zh")
}
group = "cc.unitmesh"
version = project.findProperty("mppVersion") as String? ?: "0.1.5"
// Workaround for Kotlin/JS IR internal compiler error:
// java.lang.IllegalArgumentException: List has more than one element (JsIntrinsics.getInternalFunction)
// Typically caused by multiple Kotlin stdlib klibs on the compilation classpath.
configurations.all {
resolutionStrategy {
force("org.jetbrains.kotlin:kotlin-stdlib:2.2.0")
force("org.jetbrains.kotlin:kotlin-stdlib-common:2.2.0")
force("org.jetbrains.kotlin:kotlin-stdlib-js:2.2.0")
force("org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.0")
force("org.jetbrains.kotlin:kotlin-reflect:2.2.0")
}
// Exclude kotlin-logging-android-debug to prevent duplicate classes with kotlin-logging-android
exclude(group = "io.github.oshai", module = "kotlin-logging-android-debug")
}
kotlin {
jvm {
compilerOptions {
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
freeCompilerArgs.add("-opt-in=androidx.compose.material3.ExperimentalMaterial3Api")
freeCompilerArgs.add("-opt-in=kotlin.time.ExperimentalTime")
}
}
androidTarget {
compilerOptions {
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
freeCompilerArgs.add("-opt-in=androidx.compose.material3.ExperimentalMaterial3Api")
freeCompilerArgs.add("-opt-in=kotlin.time.ExperimentalTime")
}
}
// iOS targets
listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64()
).forEach { iosTarget ->
iosTarget.binaries.framework {
baseName = "AutoDevUI"
isStatic = true
// Set bundle ID
binaryOption("bundleId", "com.phodal.autodev")
// Export dependencies to make them available in Swift
export(project(":mpp-core"))
export(compose.runtime)
export(compose.foundation)
export(compose.material3)
export(compose.ui)
}
iosTarget.compilations.getByName("main").compileTaskProvider.configure {
compilerOptions.freeCompilerArgs.add("-opt-in=kotlin.time.ExperimentalTime")
}
}
js(IR) {
// Node.js CLI only - no browser compilation
// Web UI uses pure TypeScript/React + mpp-core (similar to CLI architecture)
// nodejs {
// Configure Node.js target for CLI
// }
browser()
useCommonJs()
binaries.executable()
compilerOptions {
freeCompilerArgs.add("-opt-in=androidx.compose.material3.ExperimentalMaterial3Api")
freeCompilerArgs.add("-opt-in=kotlin.time.ExperimentalTime")
// Allow deprecated CanvasBasedWindow API until migration to ComposeViewport is complete
suppressWarnings = true
}
}
@OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class)
wasmJs {
browser {
commonWebpackConfig {
outputFileName = "mpp-ui.js"
}
}
binaries.executable()
compilerOptions {
freeCompilerArgs.add("-opt-in=androidx.compose.material3.ExperimentalMaterial3Api")
freeCompilerArgs.add("-opt-in=kotlin.time.ExperimentalTime")
// Allow deprecated CanvasBasedWindow API until migration to ComposeViewport is complete
suppressWarnings = true
}
// d8 {
// // Use d8 instead of binaryen (wasm-opt) for now
// }
}
sourceSets {
commonMain {
dependencies {
implementation(project(":mpp-core"))
implementation(project(":mpp-codegraph"))
implementation(project(":mpp-viewer"))
implementation(project(":mpp-viewer-web"))
implementation(project(":xiuper-ui"))
implementation(compose.runtime)
implementation(compose.foundation)
implementation(compose.material3)
implementation("org.jetbrains.compose.material:material-icons-core:1.7.3")
implementation(compose.ui)
implementation(compose.components.resources)
// Animation (needed for animateContentSize in LiveTerminalItem)
implementation(compose.animation)
// Rich text editor for Compose
implementation(libs.richeditor)
// Coroutines
implementation(libs.kotlinx.coroutines.core)
// DateTime for KMP
implementation(libs.kotlinx.datetime)
implementation(libs.kaml)
// JSON serialization
implementation(libs.kotlinx.serialization.json)
// FileKit - Cross-platform file picker
implementation(libs.filekit.core)
implementation(libs.filekit.dialogs)
// implementation("javax.naming:jndi:1.2.1")
// Ktor HTTP Client (for remote agent)
implementation(libs.ktor.client.core)
// i18n4k - Internationalization
implementation(libs.i18n4k.core)
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test"))
implementation(libs.kotlinx.coroutines.test)
}
}
val jvmMain by getting {
dependencies {
implementation(project(":mpp-viewer"))
implementation(project(":mpp-viewer-web"))
implementation(project(":xiuper-ui"))
implementation(project(":xiuper-e2e"))
implementation(compose.desktop.currentOs)
// compose.webview for desktop WebView support
implementation(libs.compose.webview)
// ComposeCharts - Cross-platform chart library (JVM)
implementation(libs.compose.charts)
// Lets-Plot Compose (Desktop only - macOS, Windows, Linux)
// https://github.com/JetBrains/lets-plot-compose
implementation(libs.letsplot.kotlin)
implementation(libs.letsplot.common)
implementation(libs.letsplot.canvas)
implementation(libs.letsplot.raster)
implementation(libs.letsplot.imageExport)
implementation(libs.letsplot.compose)
// WebView support (KCEF) - needed for MermaidRenderer initialization
implementation(libs.compose.webview)
// Rich text editor for Compose Desktop
implementation(libs.richeditor)
// Bonsai Tree View (JVM only)
implementation(libs.bonsai.core)
implementation(libs.bonsai.fileSystem)
// SQLDelight - JVM SQLite driver
implementation(libs.sqldelight.sqlite)
// CodeHighlight
implementation(libs.highlights)
// Multiplatform Markdown Renderer for JVM
implementation(libs.markdown.renderer.code)
implementation(libs.markdown.renderer.jvm)
implementation(libs.markdown.renderer.m3)
// plantuml
implementation(libs.plantuml)
// Logback for JVM logging backend with file storage
implementation(libs.logback.get().toString()) {
exclude(group = "javax.naming", module = "javax.naming-api")
}
// RSyntaxTextArea for syntax highlighting in JVM
implementation(libs.rsyntaxtextarea)
implementation(libs.pty4j)
implementation(libs.jediterm.core)
implementation(libs.jediterm.ui)
// Coroutines Swing for Dispatchers.Main on JVM Desktop
implementation(libs.kotlinx.coroutines.swing)
// Ktor HTTP Client CIO engine for JVM
implementation(libs.ktor.client.cio)
// i18n4k - JVM
implementation(libs.i18n4k.core.jvm)
}
}
val jvmTest by getting {
dependencies {
implementation(kotlin("test-junit"))
}
}
val androidMain by getting {
dependencies {
implementation(project(":mpp-viewer-web"))
implementation(project(":xiuper-ui")) {
exclude(group = "io.github.oshai", module = "kotlin-logging-jvm")
exclude(group = "io.github.oshai", module = "kotlin-logging-android-debug")
}
// Kotlin Logging for Android (use Android version instead of JVM version)
implementation("io.github.oshai:kotlin-logging-android:${libs.versions.kotlinLogging.get()}") {
exclude(group = "io.github.oshai", module = "kotlin-logging-android-debug")
}
implementation(libs.androidx.activity)
implementation(libs.androidx.appcompat)
implementation(libs.androidx.core)
// SLF4J Android backend (compatible with Android, replaces logback)
implementation("com.github.tony19:logback-android:3.0.0")
// ComposeCharts - Cross-platform chart library (Android)
implementation(libs.compose.charts)
// Lets-Plot Compose (Android)
// https://github.com/JetBrains/lets-plot-compose
implementation(libs.letsplot.kotlin)
implementation(libs.letsplot.common)
implementation(libs.letsplot.canvas)
implementation(libs.letsplot.raster)
implementation(libs.letsplot.compose)
// Bonsai Tree View (Android)
implementation(libs.bonsai.core)
implementation(libs.bonsai.fileSystem)
// SQLDelight - Android SQLite driver
implementation(libs.sqldelight.android)
// Multiplatform Markdown Renderer for Android
implementation(libs.markdown.renderer)
implementation(libs.markdown.renderer.m3)
// Coroutines Android for Dispatchers.Main on Android
implementation(libs.kotlinx.coroutines.android)
// Ktor HTTP Client CIO engine for Android
implementation(libs.ktor.client.cio)
// i18n4k - Android
implementation(libs.i18n4k.core.android)
}
}
val iosX64Main by getting
val iosArm64Main by getting
val iosSimulatorArm64Main by getting
iosMain {
dependencies {
// API dependencies (exported in framework)
api(project(":mpp-core"))
api(compose.runtime)
api(compose.foundation)
api(compose.material3)
api(compose.ui)
// Implementation dependencies
implementation(project(":mpp-viewer-web"))
implementation(project(":xiuper-ui"))
// ComposeCharts - Cross-platform chart library (iOS)
implementation(libs.compose.charts)
// SQLDelight - iOS SQLite driver
implementation(libs.sqldelight.native)
// Ktor HTTP Client Darwin engine for iOS
implementation(libs.ktor.client.darwin)
// Multiplatform Markdown Renderer for iOS
implementation(libs.markdown.renderer)
implementation(libs.markdown.renderer.m3)
}
}
val jsMain by getting {
dependencies {
// Node.js CLI dependencies
implementation(compose.html.core)
implementation(project(":xiuper-ui"))
// Note: mpp-viewer-web is not included for JS as WebView is not supported in Node.js CLI
// WebEdit features are only available on JVM/Desktop platforms
// SQLDelight - JS driver
implementation(libs.sqldelight.webWorker)
// Ktor HTTP Client JS engine
implementation(libs.ktor.client.js)
// i18n4k - JS
implementation(libs.i18n4k.core.js)
}
}
// Needed by generated Karma/webpack test config (see build/js/**/karma.conf.js)
// so that jsBrowserTest can load the polyfill config.
val jsTest by getting {
dependencies {
implementation(devNpm("copy-webpack-plugin", "12.0.2"))
// Required by webpack config injected via webpack.config.d/node-polyfills.js
// (used by browser tests for codegraph + git + sql worker).
implementation(npm("wasm-git", "0.0.13"))
implementation(npm("sql.js", "1.8.0"))
}
}
val wasmJsMain by getting {
dependencies {
// Force kotlin-stdlib to 2.2.0 to match compiler version
implementation("org.jetbrains.kotlin:kotlin-stdlib") {
version {
strictly(libs.versions.kotlin.get())
}
}
implementation(project(":mpp-viewer-web"))
implementation(project(":xiuper-ui"))
implementation(devNpm("copy-webpack-plugin", "12.0.2"))
implementation(npm("wasm-git", "0.0.13"))
// ComposeCharts - Cross-platform chart library (WasmJS)
implementation(libs.compose.charts)
// SQLDelight - Web Worker driver (same as JS)
implementation(npm("sql.js", "1.8.0"))
implementation(npm("@cashapp/sqldelight-sqljs-worker", "2.1.0"))
implementation(libs.sqldelight.webWorker)
implementation(libs.sqldelight.webWorker.wasmJs)
// Ktor HTTP Client JS engine (works for WASM too)
implementation(libs.ktor.client.js)
// i18n4k - WASM
implementation(libs.i18n4k.core.wasmJs)
// Multiplatform Markdown Renderer for WASM
implementation(libs.markdown.renderer)
implementation(libs.markdown.renderer.m3)
implementation(libs.markdown.renderer.code)
}
}
}
}
android {
namespace = "cc.unitmesh.devins.ui"
compileSdk = 36
defaultConfig {
applicationId = "cc.unitmesh.devins.ui"
minSdk = 24
targetSdk = 36
versionCode = 1
versionName = version.toString()
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
isCoreLibraryDesugaringEnabled = true
}
packaging {
resources {
excludes +=
setOf(
"META-INF/INDEX.LIST",
"META-INF/DEPENDENCIES",
"META-INF/LICENSE",
"META-INF/LICENSE.txt",
"META-INF/license.txt",
"META-INF/NOTICE",
"META-INF/NOTICE.txt",
"META-INF/notice.txt",
"META-INF/*.kotlin_module",
"META-INF/io.netty.versions.properties"
)
}
}
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
sourceSets["main"].res.srcDirs("src/androidMain/res")
}
// Exclude logback from Android - it uses Java 9 module APIs not available on Android
configurations.all {
if (name.contains("android", ignoreCase = true)) {
exclude(group = "ch.qos.logback", module = "logback-classic")
exclude(group = "ch.qos.logback", module = "logback-core")
}
}
dependencies {
coreLibraryDesugaring(libs.desugar)
}
compose.desktop {
application {
mainClass = "cc.unitmesh.devins.ui.MainKt"
jvmArgs += listOf(
"--add-modules", "java.naming,java.sql",
// JCEF (Java Chromium Embedded Framework) requires access to internal AWT classes
"--add-exports", "java.desktop/sun.awt=ALL-UNNAMED",
"--add-exports", "java.desktop/java.awt.peer=ALL-UNNAMED",
"--add-opens", "java.desktop/sun.awt=ALL-UNNAMED",
"--add-opens", "java.desktop/java.awt.peer=ALL-UNNAMED"
)
// macOS-specific JVM args for JCEF
if (System.getProperty("os.name").contains("Mac", ignoreCase = true)) {
jvmArgs += listOf(
"--add-opens", "java.desktop/sun.awt=ALL-UNNAMED",
"--add-opens", "java.desktop/sun.lwawt=ALL-UNNAMED",
"--add-opens", "java.desktop/sun.lwawt.macosx=ALL-UNNAMED"
)
}
nativeDistributions {
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
packageName = "AutoDev Desktop"
packageVersion = "1.0.5"
description = "AutoDev Desktop Application with Xiuper Agents Support"
copyright = "© 2024 AutoDev Team. All rights reserved."
vendor = "AutoDev Team"
modules("java.naming", "java.sql")
// File associations for .unit artifact bundles
// Note: .unit files are ZIP archives, so they can be opened with ZIP applications
fileAssociation(
mimeType = "application/zip",
extension = "unit",
description = "AutoDev Unit Bundle"
)
// Custom app icon
macOS {
bundleID = "cc.unitmesh.devins.desktop"
iconFile.set(project.file("src/jvmMain/resources/icon.icns"))
// macOS-specific: register UTI for .unit files (as ZIP archives)
infoPlist {
extraKeysRawXml = """
CFBundleDocumentTypes
CFBundleTypeName
AutoDev Unit Bundle
CFBundleTypeRole
Editor
LSHandlerRank
Owner
LSItemContentTypes
cc.unitmesh.devins.unit
UTExportedTypeDeclarations
UTTypeIdentifier
cc.unitmesh.devins.unit
UTTypeDescription
AutoDev Unit Bundle (ZIP Archive)
UTTypeConformsTo
public.zip-archive
public.archive
public.data
UTTypeTagSpecification
public.filename-extension
unit
public.mime-type
application/zip
"""
}
}
windows {
menuGroup = "AutoDev"
iconFile.set(project.file("src/jvmMain/resources/icon.ico"))
}
linux {
packageName = "autodev-desktop"
iconFile.set(project.file("src/jvmMain/resources/icon-512.png"))
}
}
buildTypes.release.proguard {
configurationFiles.from(project.file("proguard-rules.pro"))
optimize = false
obfuscate = false
}
}
}
tasks.register("printClasspath") {
doLast {
println(configurations["jvmRuntimeClasspath"].asPath)
}
}
// Task to run Remote Agent CLI
tasks.register("runRemoteAgentCli") {
group = "application"
description = "Run Remote Agent CLI (Kotlin equivalent of TypeScript server command)"
val jvmCompilation = kotlin.jvm().compilations.getByName("main")
classpath(jvmCompilation.output, configurations["jvmRuntimeClasspath"])
mainClass.set("cc.unitmesh.devins.ui.cli.RemoteAgentCli")
// Allow passing arguments from command line
// Usage: ./gradlew :mpp-ui:runRemoteAgentCli --args="--server http://localhost:8080 --project-id autocrud --task 'Write tests'"
if (project.hasProperty("args")) {
args = (project.property("args") as String).split(" ")
}
// Enable standard input for interactive mode (if needed in future)
standardInput = System.`in`
}
// Task to run Code Review Demo
tasks.register("runCodeReviewDemo") {
group = "application"
description = "Run Code Review Demo (Side-by-Side UI with Git integration)"
val jvmCompilation = kotlin.jvm().compilations.getByName("main")
classpath(jvmCompilation.output, configurations["jvmRuntimeClasspath"])
mainClass.set("cc.unitmesh.devins.ui.compose.agent.codereview.demo.CodeReviewDemoKt")
// Enable standard input
standardInput = System.`in`
}
// Task to run WebEdit Automation Test
tasks.register("runWebEditTest") {
group = "application"
description = "Run WebEdit Automation Test (Automated testing of WebEdit features)"
val jvmCompilation = kotlin.jvm().compilations.getByName("main")
classpath(jvmCompilation.output, configurations["jvmRuntimeClasspath"])
mainClass.set("cc.unitmesh.devins.ui.webedit.WebEditAutomationTestKt")
// Enable standard input
standardInput = System.`in`
}
// Task to run WebEdit Vision Preview (Screenshot + GLM-4.6V testing)
tasks.register("runVisionPreview") {
group = "application"
description = "Run WebEdit Vision Preview (Test screenshot capture + vision LLM fallback)"
val jvmCompilation = kotlin.jvm().compilations.getByName("main")
classpath(jvmCompilation.output, configurations["jvmRuntimeClasspath"])
mainClass.set("cc.unitmesh.devins.ui.webedit.WebEditVisionPreviewKt")
// Enable standard input
standardInput = System.`in`
}
// Task to run Document CLI
tasks.register("runDocumentCli") {
group = "application"
description = "Run Document CLI"
val jvmCompilation = kotlin.jvm().compilations.getByName("main")
classpath(jvmCompilation.output, configurations["jvmRuntimeClasspath"])
mainClass.set("cc.unitmesh.server.cli.DocumentCli")
// Pass properties - use docProjectPath to avoid conflict with Gradle's projectPath
if (project.hasProperty("docProjectPath")) {
systemProperty("projectPath", project.property("docProjectPath") as String)
}
if (project.hasProperty("docQuery")) {
systemProperty("query", project.property("docQuery") as String)
}
if (project.hasProperty("docPath")) {
systemProperty("documentPath", project.property("docPath") as String)
}
// New parameters for feature tree mode
if (project.hasProperty("docMode")) {
systemProperty("mode", project.property("docMode") as String)
}
if (project.hasProperty("docLanguage")) {
systemProperty("language", project.property("docLanguage") as String)
}
standardInput = System.`in`
}
// Task to run PlotDSL CLI
tasks.register("runPlotDSLCli") {
group = "application"
description = "Run PlotDSL CLI for generating statistical charts from natural language"
val jvmCompilation = kotlin.jvm().compilations.getByName("main")
classpath(jvmCompilation.output, configurations["jvmRuntimeClasspath"])
mainClass.set("cc.unitmesh.server.cli.PlotDSLCli")
// Pass properties
if (project.hasProperty("plotDescription")) {
systemProperty("description", project.property("plotDescription") as String)
}
if (project.hasProperty("plotChartType")) {
systemProperty("chartType", project.property("plotChartType") as String)
}
if (project.hasProperty("plotTheme")) {
systemProperty("theme", project.property("plotTheme") as String)
}
standardInput = System.`in`
}
// Task to run Coding CLI
tasks.register("runCodingCli") {
group = "application"
description = "Run Coding Agent CLI for autonomous coding tasks"
val jvmCompilation = kotlin.jvm().compilations.getByName("main")
classpath(jvmCompilation.output, configurations["jvmRuntimeClasspath"])
mainClass.set("cc.unitmesh.server.cli.CodingCli")
// Pass properties - use codingProjectPath to avoid conflict with Gradle's projectPath
if (project.hasProperty("codingProjectPath")) {
systemProperty("projectPath", project.property("codingProjectPath") as String)
}
if (project.hasProperty("codingTask")) {
systemProperty("task", project.property("codingTask") as String)
}
if (project.hasProperty("codingMaxIterations")) {
systemProperty("maxIterations", project.property("codingMaxIterations") as String)
}
standardInput = System.`in`
}
// Task to run Renderer Batch Test
tasks.register("runBatchTest") {
group = "application"
description = "Test ComposeRenderer batching functionality"
val jvmCompilation = kotlin.jvm().compilations.getByName("main")
classpath(jvmCompilation.output, configurations["jvmRuntimeClasspath"])
mainClass.set("cc.unitmesh.server.cli.RendererBatchTest")
standardInput = System.`in`
}
// Task to capture ACP responses
tasks.register("runAcpCapture") {
group = "application"
description = "Capture ACP agent responses for test cases"
val jvmCompilation = kotlin.jvm().compilations.getByName("main")
classpath(jvmCompilation.output, configurations["jvmRuntimeClasspath"])
mainClass.set("cc.unitmesh.server.cli.AcpCaptureCli")
if (project.hasProperty("acpPrompt")) {
systemProperty("acpPrompt", project.property("acpPrompt") as String)
}
if (project.hasProperty("acpAgentKey")) {
systemProperty("acpAgentKey", project.property("acpAgentKey") as String)
}
if (project.hasProperty("acpCwd")) {
systemProperty("acpCwd", project.property("acpCwd") as String)
}
standardInput = System.`in`
}
// Task to replay captured ACP events
tasks.register("runAcpReplay") {
group = "application"
description = "Replay captured ACP events to test renderers"
val jvmCompilation = kotlin.jvm().compilations.getByName("main")
classpath(jvmCompilation.output, configurations["jvmRuntimeClasspath"])
mainClass.set("cc.unitmesh.server.cli.AcpReplayCli")
if (project.hasProperty("acpCapture")) {
systemProperty("acpCapture", project.property("acpCapture") as String)
}
standardInput = System.`in`
}
// Task to run E2E Test CLI
tasks.register("runE2ECli") {
group = "application"
description = "Run E2E Test Agent CLI for AI-driven test scenario generation"
val jvmCompilation = kotlin.jvm().compilations.getByName("main")
classpath(jvmCompilation.output, configurations["jvmRuntimeClasspath"])
mainClass.set("cc.unitmesh.server.cli.E2ECli")
// Pass properties
if (project.hasProperty("e2eUrl")) {
systemProperty("e2eUrl", project.property("e2eUrl") as String)
}
if (project.hasProperty("e2eGoal")) {
systemProperty("e2eGoal", project.property("e2eGoal") as String)
}
if (project.hasProperty("e2eOutput")) {
systemProperty("e2eOutput", project.property("e2eOutput") as String)
}
standardInput = System.`in`
}
// Task to run Review CLI
tasks.register("runReviewCli") {
group = "application"
description = "Run Code Review CLI (Fix Generation with CodingAgent)"
val jvmCompilation = kotlin.jvm().compilations.getByName("main")
classpath(jvmCompilation.output, configurations["jvmRuntimeClasspath"])
mainClass.set("cc.unitmesh.server.cli.ReviewCli")
// Pass properties
if (project.hasProperty("reviewProjectPath")) {
systemProperty("reviewProjectPath", project.property("reviewProjectPath") as String)
}
if (project.hasProperty("reviewAnalysis")) {
systemProperty("reviewAnalysis", project.property("reviewAnalysis") as String)
}
if (project.hasProperty("reviewPatch")) {
systemProperty("reviewPatch", project.property("reviewPatch") as String)
}
if (project.hasProperty("reviewCommitHash")) {
systemProperty("reviewCommitHash", project.property("reviewCommitHash") as String)
}
if (project.hasProperty("reviewUserFeedback")) {
systemProperty("reviewUserFeedback", project.property("reviewUserFeedback") as String)
}
if (project.hasProperty("reviewLanguage")) {
systemProperty("reviewLanguage", project.property("reviewLanguage") as String)
}
standardInput = System.`in`
}
// Task to run Domain Dictionary Deep Research CLI
tasks.register("runDomainDictCli") {
group = "application"
description = "Run Domain Dictionary Deep Research CLI (analyze agent output)"
val jvmCompilation = kotlin.jvm().compilations.getByName("main")
classpath(jvmCompilation.output, configurations["jvmRuntimeClasspath"])
mainClass.set("cc.unitmesh.server.cli.DomainDictCli")
// Pass properties
if (project.hasProperty("domainProjectPath")) {
systemProperty("domainProjectPath", project.property("domainProjectPath") as String)
}
if (project.hasProperty("domainQuery")) {
systemProperty("domainQuery", project.property("domainQuery") as String)
}
if (project.hasProperty("domainFocusArea")) {
systemProperty("domainFocusArea", project.property("domainFocusArea") as String)
}
standardInput = System.`in`
}
// Task to run ChatDB CLI (Text2SQL Agent)
tasks.register("runChatDBCli") {
group = "application"
description = "Run ChatDB CLI (Text2SQL Agent with Schema Linking and Revise Agent)"
val jvmCompilation = kotlin.jvm().compilations.getByName("main")
classpath(jvmCompilation.output, configurations["jvmRuntimeClasspath"])
mainClass.set("cc.unitmesh.server.cli.ChatDBCli")
// Pass database connection properties
if (project.hasProperty("dbHost")) {
systemProperty("dbHost", project.property("dbHost") as String)
}
if (project.hasProperty("dbPort")) {
systemProperty("dbPort", project.property("dbPort") as String)
}
if (project.hasProperty("dbName")) {
systemProperty("dbName", project.property("dbName") as String)
}
if (project.hasProperty("dbUser")) {
systemProperty("dbUser", project.property("dbUser") as String)
}
if (project.hasProperty("dbPassword")) {
systemProperty("dbPassword", project.property("dbPassword") as String)
}
if (project.hasProperty("dbDialect")) {
systemProperty("dbDialect", project.property("dbDialect") as String)
}
if (project.hasProperty("dbQuery")) {
systemProperty("dbQuery", project.property("dbQuery") as String)
}
if (project.hasProperty("generateVisualization")) {
systemProperty("generateVisualization", project.property("generateVisualization") as String)
}
if (project.hasProperty("maxRows")) {
systemProperty("maxRows", project.property("maxRows") as String)
}
standardInput = System.`in`
}
// Task to run Vision CLI (Multimodal GLM-4.6V)
tasks.register("runVisionCli") {
group = "application"
description = "Run Vision CLI (Multimodal image understanding with GLM-4.6V)"
val jvmCompilation = kotlin.jvm().compilations.getByName("main")
classpath(jvmCompilation.output, configurations["jvmRuntimeClasspath"])
mainClass.set("cc.unitmesh.server.cli.VisionCli")
// Image and prompt properties
if (project.hasProperty("visionImage")) {
systemProperty("visionImage", project.property("visionImage") as String)
}
if (project.hasProperty("visionPrompt")) {
systemProperty("visionPrompt", project.property("visionPrompt") as String)
}
if (project.hasProperty("enableThinking")) {
systemProperty("enableThinking", project.property("enableThinking") as String)
}
// Tencent COS properties
if (project.hasProperty("cosSecretId")) {
systemProperty("cosSecretId", project.property("cosSecretId") as String)
}
if (project.hasProperty("cosSecretKey")) {
systemProperty("cosSecretKey", project.property("cosSecretKey") as String)
}
if (project.hasProperty("cosBucket")) {
systemProperty("cosBucket", project.property("cosBucket") as String)
}
if (project.hasProperty("cosRegion")) {
systemProperty("cosRegion", project.property("cosRegion") as String)
}
standardInput = System.`in`
}
// Task to test COS bucket region
tasks.register("runCosTest") {
group = "application"
description = "Test which region a Tencent COS bucket is in"
val jvmCompilation = kotlin.jvm().compilations.getByName("main")
classpath(jvmCompilation.output, configurations["jvmRuntimeClasspath"])
mainClass.set("cc.unitmesh.server.cli.CosTestCli")
// Pass bucket name as argument
if (project.hasProperty("bucket")) {
args(project.property("bucket") as String)
}
}
// Ktlint configuration
configure {
version.set("1.0.1")
android.set(true)
outputToConsole.set(true)
ignoreFailures.set(true)
filter {
exclude("**/generated/**")
exclude("**/build/**")
}
}
// Note: We enable wasm-opt optimizer for production webpack builds
// If this causes issues, use wasmJsBrowserDevelopmentExecutableDistribution instead
// tasks.named("compileProductionExecutableKotlinWasmJsOptimize") {
// enabled = false
// }
// Ensure wasmJsBrowserDistribution runs webpack before copying files
tasks.named("wasmJsBrowserDistribution") {
dependsOn("wasmJsBrowserProductionWebpack")
}
// Force webpack to run (remove onlyIf condition that skips it)
tasks.named("wasmJsBrowserProductionWebpack") {
onlyIf { true }
}
// =============================================================================
// Auto-download fonts for WASM UTF-8 support (not committed to Git)
// =============================================================================
/**
* Task to download Noto Sans font for comprehensive UTF-8 support in WASM
*
* Downloads a lightweight Noto Sans variant that supports:
* - Latin, Cyrillic, Greek
* - Basic punctuation and symbols
* - Some emoji support
*
* For full CJK (Chinese, Japanese, Korean) support, consider using Noto Sans CJK
* which is larger (~15-20MB per variant).
*/
abstract class DownloadWasmFontsTask : DefaultTask() {
@get:OutputDirectory
abstract val fontDir: DirectoryProperty
@get:Input
abstract val useCJKFont: Property
@TaskAction
fun download() {
val fontDirectory = fontDir.get().asFile
fontDirectory.mkdirs()
val notoSansFile = File(fontDirectory, "NotoSans-Regular.ttf")
val notoSansCJKFile = File(fontDirectory, "NotoSansSC-Regular.ttf")
// Skip if fonts already exist
if (notoSansFile.exists() || notoSansCJKFile.exists()) {
println("Fonts already downloaded, skipping...")
return
}
if (useCJKFont.get()) {
// Download Noto Sans CJK SC (Simplified Chinese) TTF for full UTF-8 support
println("Downloading Noto Sans CJK SC Variable TTF (Simplified Chinese)...")
println("This font supports: Latin, Chinese, Japanese, Korean, Emoji")
println("Size: ~17MB (Variable TTF format)")
// Use GitHub raw content for reliable TTF download
val cjkUrl = "https://github.com/notofonts/noto-cjk/raw/main/Sans/Variable/TTF/Subset/NotoSansSC-VF.ttf"
try {
ant.invokeMethod("get", mapOf(
"src" to cjkUrl,
"dest" to notoSansCJKFile,
"verbose" to true,
"usetimestamp" to true,
"retries" to 3
))
println("✅ Downloaded: ${notoSansCJKFile.name} (${notoSansCJKFile.length() / 1024 / 1024}MB)")
} catch (e: Exception) {
println("❌ Failed to download CJK TTF font: ${e.message}")
println("You can manually download from: $cjkUrl")
throw e
}
} else {
// Download Noto Sans (lighter weight, basic UTF-8)
println("Downloading Noto Sans TTF (lightweight)...")
println("This font supports: Latin, Cyrillic, Greek, basic symbols")
println("Size: ~500KB")
val notoSansUrl = "https://github.com/googlefonts/noto-fonts/raw/main/hinted/ttf/NotoSans/NotoSans-Regular.ttf"
try {
ant.invokeMethod("get", mapOf(
"src" to notoSansUrl,
"dest" to notoSansFile,
"verbose" to true,
"usetimestamp" to true,
"retries" to 3
))
println("✅ Downloaded: ${notoSansFile.name} (${notoSansFile.length() / 1024}KB)")
println("ℹ️ For CJK support, run: ./gradlew downloadWasmFonts -PuseCJKFont=true")
} catch (e: Exception) {
println("❌ Failed to download Noto Sans font: ${e.message}")
println("You can manually download from: $notoSansUrl")
throw e
}
}
}
}
tasks.register("downloadWasmFonts") {
group = "build"
description = "Download fonts for WASM UTF-8 support (not committed to Git)"
// Fonts are only needed for WASM platform, so download to wasmJsMain
fontDir.set(file("src/wasmJsMain/composeResources/font"))
useCJKFont.set(project.findProperty("useCJKFont")?.toString()?.toBoolean() ?: true)
}
// Auto-download fonts before resource processing
tasks.matching {
it.name.contains("copyNonXmlValueResources") ||
it.name.contains("prepareComposeResources") ||
it.name.contains("generateComposeResClass")
}.configureEach {
dependsOn("downloadWasmFonts")
}
// Also download before WASM compilation
tasks.matching { it.name.contains("compileKotlinWasm") }.configureEach {
mustRunAfter("downloadWasmFonts")
}
// Configure JVM args for all JavaExec tasks (required for KCEF/WebView on desktop)
// This ensures that when running with `./gradlew :mpp-ui:run`, JCEF can access AWT internals
afterEvaluate {
tasks.withType {
jvmArgs("--add-opens", "java.desktop/sun.awt=ALL-UNNAMED")
jvmArgs("--add-opens", "java.desktop/java.awt.peer=ALL-UNNAMED")
if (System.getProperty("os.name").contains("Mac")) {
jvmArgs("--add-opens", "java.desktop/sun.awt=ALL-UNNAMED")
jvmArgs("--add-opens", "java.desktop/sun.lwawt=ALL-UNNAMED")
jvmArgs("--add-opens", "java.desktop/sun.lwawt.macosx=ALL-UNNAMED")
}
}
}
// JVM CLI: Render/debug NanoDSL text interpolation for generated cases
tasks.register("runNanoDslTextRenderCli") {
group = "application"
description = "Parse NanoDSL -> init state -> print raw vs rendered string props (writes render-report.txt by default)"
mainClass.set("cc.unitmesh.devins.ui.nano.cli.NanoDslTextRenderCli")
dependsOn("jvmMainClasses")
classpath = kotlin.jvm().compilations.getByName("main").runtimeDependencyFiles +
files(kotlin.jvm().compilations.getByName("main").output.classesDirs)
}
// Task to run NanoDSL Demo preview
tasks.register("runNanoDSLDemo") {
group = "application"
description = "Run NanoDSL Demo preview window"
mainClass.set("cc.unitmesh.devins.ui.nano.NanoDSLDemoPreviewKt")
classpath = kotlin.jvm().compilations.getByName("main").runtimeDependencyFiles +
files(kotlin.jvm().compilations.getByName("main").output.classesDirs)
}
// Task to run Artifact CLI (HTML/JS artifact generation testing)
tasks.register("runArtifactCli") {
group = "application"
description = "Run Artifact Agent CLI for testing HTML/JS artifact generation"
val jvmCompilation = kotlin.jvm().compilations.getByName("main")
classpath(jvmCompilation.output, configurations["jvmRuntimeClasspath"])
mainClass.set("cc.unitmesh.server.cli.ArtifactCli")
// Pass properties
if (project.hasProperty("artifactPrompt")) {
systemProperty("artifactPrompt", project.property("artifactPrompt") as String)
}
if (project.hasProperty("artifactScenario")) {
systemProperty("artifactScenario", project.property("artifactScenario") as String)
}
if (project.hasProperty("artifactOutput")) {
systemProperty("artifactOutput", project.property("artifactOutput") as String)
}
if (project.hasProperty("artifactLanguage")) {
systemProperty("artifactLanguage", project.property("artifactLanguage") as String)
}
standardInput = System.`in`
}
// Task to debug ACP issues (wildcard, session, bash)
tasks.register("runAcpDebug") {
group = "autodev"
description = "Debug ACP agent issues (wildcard, session, bash)"
val jvmCompilation = kotlin.jvm().compilations.getByName("main")
classpath(jvmCompilation.output, configurations["jvmRuntimeClasspath"])
mainClass.set("cc.unitmesh.server.cli.AcpDebugCli")
// Pass command line arguments
// Usage: ./gradlew :mpp-ui:runAcpDebug --args="--agent=Gemini --test=wildcard"
if (project.hasProperty("args")) {
args = (project.property("args") as String).split(" ")
}
standardInput = System.`in`
}