mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-08-28 19:11:03 +08:00
feat(jetbrains): add CLI binary bundling and build pipeline
Adds a build system that compiles Kilo CLI binaries and packages them
into the backend jar at /cli/{os}/kilo for runtime extraction.
- script/build.ts: orchestrates CLI build + Gradle plugin build
- Local mode (bun run build): builds current platform only
- Production mode (bun run build:production): requires all 6 platforms
- Gradle checkCli task validates binaries before processResources
- Turbo integration via @kilocode/kilo-jetbrains#build
- README with setup and build instructions
This commit is contained in:
@@ -214,6 +214,9 @@
|
||||
"typescript": "catalog:",
|
||||
},
|
||||
},
|
||||
"packages/kilo-jetbrains": {
|
||||
"name": "@kilocode/kilo-jetbrains",
|
||||
},
|
||||
"packages/kilo-telemetry": {
|
||||
"name": "@kilocode/kilo-telemetry",
|
||||
"version": "7.1.17",
|
||||
@@ -1211,6 +1214,8 @@
|
||||
|
||||
"@kilocode/kilo-i18n": ["@kilocode/kilo-i18n@workspace:packages/kilo-i18n"],
|
||||
|
||||
"@kilocode/kilo-jetbrains": ["@kilocode/kilo-jetbrains@workspace:packages/kilo-jetbrains"],
|
||||
|
||||
"@kilocode/kilo-telemetry": ["@kilocode/kilo-telemetry@workspace:packages/kilo-telemetry"],
|
||||
|
||||
"@kilocode/kilo-ui": ["@kilocode/kilo-ui@workspace:packages/kilo-ui"],
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
# Kilo JetBrains
|
||||
|
||||
AI coding agent plugin for JetBrains IDEs.
|
||||
|
||||
---
|
||||
|
||||
## Set up your environment
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- **Bun** -- used to build CLI binaries and run build scripts
|
||||
- **JDK 21+** -- required by Gradle and the IntelliJ Platform SDK
|
||||
- **IntelliJ IDEA** -- to run the plugin in a sandboxed IDE
|
||||
|
||||
---
|
||||
|
||||
## Open in IntelliJ
|
||||
|
||||
When you open the monorepo root in IntelliJ IDEA, the Gradle project at `packages/kilo-jetbrains/` should be auto-detected via `.idea/gradle.xml`. If not, link it manually: **File > Settings > Build Tools > Gradle > +** and select `packages/kilo-jetbrains/settings.gradle.kts`.
|
||||
|
||||
---
|
||||
|
||||
## Build locally
|
||||
|
||||
From `packages/kilo-jetbrains/`:
|
||||
|
||||
```
|
||||
bun run build
|
||||
```
|
||||
|
||||
This builds the CLI binary for your current OS/arch only, copies it into the backend module resources, and runs `./gradlew buildPlugin`. The plugin archive is output to `build/distributions/`.
|
||||
|
||||
Or via Turbo from the repo root:
|
||||
|
||||
```
|
||||
bun turbo build --filter=@kilocode/kilo-jetbrains
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Build for production
|
||||
|
||||
From `packages/kilo-jetbrains/`:
|
||||
|
||||
```
|
||||
bun run build:production
|
||||
```
|
||||
|
||||
This builds CLI binaries for all 6 desktop platforms (darwin-arm64, darwin-x64, linux-arm64, linux-x64, windows-x64, windows-arm64), copies them all into the backend jar, and fails if any are missing. Gradle also validates all platforms are present via `-Pproduction=true`.
|
||||
|
||||
---
|
||||
|
||||
## Run the plugin
|
||||
|
||||
After building, use the `runIde` Gradle task (available in the Gradle tool window or via the "Run JetBrains Plugin" run configuration) to launch a sandboxed IntelliJ instance with the plugin installed.
|
||||
|
||||
Note: `runIde` does not build CLI binaries -- run `bun run build` at least once before using it. Subsequent Kotlin/Gradle changes can be iterated with `runIde` directly.
|
||||
|
||||
---
|
||||
|
||||
## Run Gradle directly
|
||||
|
||||
You can run `./gradlew buildPlugin` directly if CLI binaries are already in `backend/build/generated/cli/`. Gradle will fail with a clear error if they are missing.
|
||||
|
||||
For production verification:
|
||||
|
||||
```
|
||||
./gradlew buildPlugin -Pproduction=true
|
||||
```
|
||||
@@ -6,6 +6,55 @@ kotlin {
|
||||
jvmToolchain(21)
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
resources.srcDir(layout.buildDirectory.dir("generated/cli"))
|
||||
}
|
||||
}
|
||||
|
||||
val cliDir = layout.buildDirectory.dir("generated/cli/cli")
|
||||
val production = providers.gradleProperty("production").map { it.toBoolean() }.orElse(false)
|
||||
|
||||
val requiredPlatforms = listOf(
|
||||
"darwin-arm64",
|
||||
"darwin-x64",
|
||||
"linux-arm64",
|
||||
"linux-x64",
|
||||
"windows-x64",
|
||||
"windows-arm64",
|
||||
)
|
||||
|
||||
val checkCli by tasks.registering {
|
||||
description = "Verify CLI binaries exist before building"
|
||||
val dir = cliDir.map { it.asFile }
|
||||
val prod = production.get()
|
||||
val platforms = requiredPlatforms.toList()
|
||||
doLast {
|
||||
val resolved = dir.get()
|
||||
if (!resolved.exists() || resolved.listFiles()?.isEmpty() != false) {
|
||||
throw GradleException(
|
||||
"CLI binaries not found at ${resolved.absolutePath}.\n" +
|
||||
"Run 'bun run build' from packages/kilo-jetbrains/ to build CLI and plugin together."
|
||||
)
|
||||
}
|
||||
if (prod) {
|
||||
val present = resolved.listFiles()?.map { it.name }?.toSet() ?: emptySet()
|
||||
val missing = platforms.filter { it !in present }
|
||||
if (missing.isNotEmpty()) {
|
||||
throw GradleException(
|
||||
"Production build requires all platform CLI binaries.\n" +
|
||||
"Missing: ${missing.joinToString(", ")}\n" +
|
||||
"Run 'bun run build:production' to build all platforms."
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.processResources {
|
||||
dependsOn(checkCli)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
intellijPlatform {
|
||||
intellijIdea(libs.versions.intellij.platform)
|
||||
|
||||
@@ -50,3 +50,5 @@ intellijPlatform {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "@kilocode/kilo-jetbrains",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "bun script/build.ts",
|
||||
"build:production": "bun script/build.ts --production"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env bun
|
||||
|
||||
/**
|
||||
* Build the Kilo JetBrains plugin.
|
||||
*
|
||||
* Usage:
|
||||
* bun script/build.ts # Local build — only current platform binary required
|
||||
* bun script/build.ts --production # Production build — all 6 platform binaries required
|
||||
*
|
||||
* Steps:
|
||||
* 1. Builds CLI binaries (or uses prebuilt ones from dist/).
|
||||
* Local: builds only current platform (--single).
|
||||
* Production: builds all platforms.
|
||||
* 2. Copies them into backend/build/generated/cli/cli/{os}/kilo[.exe]
|
||||
* so they end up inside the backend jar at /cli/{os}/kilo.
|
||||
* 3. Invokes Gradle to build the plugin.
|
||||
*/
|
||||
|
||||
import { $ } from "bun"
|
||||
import { join, relative } from "node:path"
|
||||
import { existsSync, mkdirSync, chmodSync, cpSync, rmSync } from "node:fs"
|
||||
|
||||
const production = process.argv.includes("--production")
|
||||
|
||||
const root = join(import.meta.dir, "..")
|
||||
const packages = join(root, "..")
|
||||
const opencodeDir = join(packages, "opencode")
|
||||
const distDir = join(opencodeDir, "dist")
|
||||
const cliDir = join(root, "backend", "build", "generated", "cli", "cli")
|
||||
|
||||
/** All desktop platforms. */
|
||||
const platforms = [
|
||||
{ os: "darwin-arm64", exe: "kilo" },
|
||||
{ os: "darwin-x64", exe: "kilo" },
|
||||
{ os: "linux-arm64", exe: "kilo" },
|
||||
{ os: "linux-x64", exe: "kilo" },
|
||||
{ os: "windows-x64", exe: "kilo.exe" },
|
||||
{ os: "windows-arm64", exe: "kilo.exe" },
|
||||
] as const
|
||||
|
||||
function localPlatformTag(): string {
|
||||
const os = process.platform === "win32" ? "windows" : process.platform
|
||||
return `${os}-${process.arch}`
|
||||
}
|
||||
|
||||
function log(msg: string) {
|
||||
console.log(`[jetbrains-build] ${msg}`)
|
||||
}
|
||||
|
||||
function distBinPath(os: string, exe: string): string {
|
||||
return join(distDir, `@kilocode/cli-${os}`, "bin", exe)
|
||||
}
|
||||
|
||||
function hasDist(): boolean {
|
||||
if (production) {
|
||||
return platforms.every((p) => existsSync(distBinPath(p.os, p.exe)))
|
||||
}
|
||||
const tag = localPlatformTag()
|
||||
const local = platforms.find((p) => p.os === tag)
|
||||
return local ? existsSync(distBinPath(local.os, local.exe)) : false
|
||||
}
|
||||
|
||||
async function prepareCli() {
|
||||
const mode = production ? "production" : "local"
|
||||
log(`Mode: ${mode}`)
|
||||
|
||||
if (!hasDist()) {
|
||||
log("Building CLI binaries via opencode...")
|
||||
if (!existsSync(join(opencodeDir, "package.json"))) {
|
||||
throw new Error(`Expected opencode package at ${opencodeDir}`)
|
||||
}
|
||||
const args = production ? [] : ["--single"]
|
||||
await $`bun run build ${args}`.cwd(opencodeDir)
|
||||
} else {
|
||||
log("Found prebuilt CLI binaries in opencode/dist/, skipping CLI build")
|
||||
}
|
||||
|
||||
if (existsSync(cliDir)) {
|
||||
rmSync(cliDir, { recursive: true })
|
||||
}
|
||||
|
||||
const missing: string[] = []
|
||||
let copied = 0
|
||||
for (const p of platforms) {
|
||||
const src = distBinPath(p.os, p.exe)
|
||||
if (!existsSync(src)) {
|
||||
missing.push(p.os)
|
||||
continue
|
||||
}
|
||||
|
||||
const dir = join(cliDir, p.os)
|
||||
mkdirSync(dir, { recursive: true })
|
||||
const dest = join(dir, p.exe)
|
||||
cpSync(src, dest)
|
||||
chmodSync(dest, 0o755)
|
||||
copied++
|
||||
log(`Copied ${relative(root, src)} -> ${relative(root, dest)}`)
|
||||
}
|
||||
|
||||
if (copied === 0) {
|
||||
throw new Error("No CLI binaries were copied — the build cannot proceed")
|
||||
}
|
||||
|
||||
if (production && missing.length > 0) {
|
||||
throw new Error(`Production build requires all platform binaries. Missing: ${missing.join(", ")}`)
|
||||
}
|
||||
|
||||
if (missing.length > 0) {
|
||||
log(`Skipped ${missing.length} platforms (not needed for local build): ${missing.join(", ")}`)
|
||||
}
|
||||
|
||||
log(`Copied ${copied}/${platforms.length} platform binaries`)
|
||||
}
|
||||
|
||||
async function buildPlugin() {
|
||||
log("Building JetBrains plugin via Gradle...")
|
||||
const args = production ? ["-Pproduction=true"] : []
|
||||
await $`./gradlew buildPlugin ${args}`.cwd(root)
|
||||
log("Done. Plugin archive is in build/distributions/")
|
||||
}
|
||||
|
||||
try {
|
||||
await prepareCli()
|
||||
await buildPlugin()
|
||||
} catch (err) {
|
||||
console.error(`[jetbrains-build] ERROR: ${err instanceof Error ? err.message : String(err)}`)
|
||||
process.exit(1)
|
||||
}
|
||||
@@ -22,6 +22,10 @@
|
||||
"dependsOn": ["^build"],
|
||||
"outputs": [".next/**"],
|
||||
"env": ["POSTHOG_API_KEY", "FREE_TIER_AMOUNT", "NEXT_PUBLIC_POSTHOG_KEY", "NEXT_PUBLIC_POSTHOG_HOST"]
|
||||
},
|
||||
"@kilocode/kilo-jetbrains#build": {
|
||||
"dependsOn": ["@kilocode/cli#build"],
|
||||
"outputs": ["build/distributions/**"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user