diff --git a/packages/kilo-vscode/package.json b/packages/kilo-vscode/package.json index ceecdd9dcf1..b59f3a24f14 100644 --- a/packages/kilo-vscode/package.json +++ b/packages/kilo-vscode/package.json @@ -99,11 +99,12 @@ }, "scripts": { "vscode:prepublish": "pnpm run package", - "compile": "pnpm run check-types && pnpm run lint && node esbuild.js", + "prepare:cli-binary": "node scripts/prepare-cli-binary.mjs", + "compile": "pnpm run prepare:cli-binary && pnpm run check-types && pnpm run lint && node esbuild.js", "watch": "npm-run-all -p watch:*", - "watch:esbuild": "node esbuild.js --watch", + "watch:esbuild": "pnpm run prepare:cli-binary && node esbuild.js --watch", "watch:tsc": "tsc --noEmit --watch --project tsconfig.json", - "package": "pnpm run check-types && pnpm run lint && node esbuild.js --production", + "package": "pnpm run prepare:cli-binary && pnpm run check-types && pnpm run lint && node esbuild.js --production", "compile-tests": "tsc -p . --outDir out", "watch-tests": "tsc -p . -w --outDir out", "pretest": "pnpm run compile-tests && pnpm run compile && pnpm run lint", diff --git a/packages/kilo-vscode/scripts/prepare-cli-binary.mjs b/packages/kilo-vscode/scripts/prepare-cli-binary.mjs new file mode 100644 index 00000000000..adff96a08a5 --- /dev/null +++ b/packages/kilo-vscode/scripts/prepare-cli-binary.mjs @@ -0,0 +1,120 @@ +import { existsSync, mkdirSync, readdirSync, statSync, copyFileSync, chmodSync } from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { execSync } from "node:child_process"; + +/** + * Ensures the VS Code extension has a CLI binary at `packages/kilo-vscode/bin/kilo`. + * + * Strategy: + * 1) If `bin/kilo` already exists -> ok. + * 2) Else try to locate a prebuilt binary produced by `packages/opencode` build. + * 3) Else try to build it via `bun run build --single` in `packages/opencode`. + * 4) Copy the resulting binary into `packages/kilo-vscode/bin/kilo` and chmod +x. + * + * This script is intended to be run from `packages/kilo-vscode` as part of build/package. + */ + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const kiloVscodeDir = path.resolve(__dirname, ".."); +const repoRootDir = path.resolve(kiloVscodeDir, ".."); +const opencodeDir = path.resolve(repoRootDir, "opencode"); + +const targetBinDir = path.resolve(kiloVscodeDir, "bin"); +const targetBinPath = path.resolve(targetBinDir, "kilo"); + +function log(msg) { + // Keep logs machine-grep friendly in CI + console.log(`[prepare-cli-binary] ${msg}`); +} + +function findKiloBinaryInOpencodeDist() { + const distDir = path.resolve(opencodeDir, "dist"); + if (!existsSync(distDir)) return null; + + // Expected: packages/opencode/dist/@kilocode/cli-/bin/kilo + // But keep it flexible: find any dist/**/bin/kilo + /** @type {string[]} */ + const queue = [distDir]; + while (queue.length) { + const dir = queue.pop(); + if (!dir) continue; + let entries; + try { + entries = readdirSync(dir, { withFileTypes: true }); + } catch { + continue; + } + for (const e of entries) { + const p = path.join(dir, e.name); + if (e.isDirectory()) { + queue.push(p); + continue; + } + if (e.isFile() && e.name === "kilo" && path.basename(path.dirname(p)) === "bin") { + return p; + } + } + } + return null; +} + +function ensureBuiltBinary() { + const found = findKiloBinaryInOpencodeDist(); + if (found) return found; + + log(`No prebuilt binary found under ${path.relative(kiloVscodeDir, path.resolve(opencodeDir, "dist"))} - attempting build via bun.`); + + try { + execSync("bun --version", { stdio: "ignore" }); + } catch { + throw new Error( + `Bun is required to build the CLI binary, but was not found on PATH. ` + + `Install bun, or build the CLI separately in ${opencodeDir} and re-run.` + ); + } + + // Build using the opencode package script. + execSync("bun run build --single", { + cwd: opencodeDir, + stdio: "inherit", + env: process.env, + }); + + const built = findKiloBinaryInOpencodeDist(); + if (!built) { + throw new Error( + `CLI build completed but no binary was found in ${path.resolve(opencodeDir, "dist")} (expected dist/**/bin/kilo).` + ); + } + return built; +} + +function main() { + if (existsSync(targetBinPath)) { + const st = statSync(targetBinPath); + log(`CLI binary already present at ${path.relative(kiloVscodeDir, targetBinPath)} (${Math.round(st.size / 1024 / 1024)}MB).`); + return; + } + + if (!existsSync(opencodeDir)) { + throw new Error(`Expected opencode package at ${opencodeDir}, but it does not exist.`); + } + + const sourceBinPath = ensureBuiltBinary(); + mkdirSync(targetBinDir, { recursive: true }); + copyFileSync(sourceBinPath, targetBinPath); + chmodSync(targetBinPath, 0o755); + + log(`Copied CLI binary from ${path.relative(repoRootDir, sourceBinPath)} -> ${path.relative(kiloVscodeDir, targetBinPath)}`); +} + +try { + main(); +} catch (err) { + console.error(`[prepare-cli-binary] ERROR: ${err instanceof Error ? err.message : String(err)}`); + process.exit(1); +} +