Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fix-kilo-npm-upgrade.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@kilocode/cli": patch
---

Fix upgrades to resolve Kilo CLI packages and releases instead of OpenCode packages and versions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions packages/kilo-docs/source-links.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@
<!-- packages/kilo-vscode/webview-ui/src/components/migration/MigrationWizard.tsx -->
- <https://kilo.ai/gateway>
<!-- packages/opencode/src/kilocode/cli/cmd/tui/component/dialog-provider.tsx -->
- <https://kilo.ai/install>
<!-- packages/opencode/src/kilocode/installation/index.ts -->
- <https://kilo.ai/kiloclaw>
<!-- packages/kilo-vscode/webview-ui/kiloclaw/components/SetupView.tsx -->
<!-- packages/opencode/src/kilocode/components/dialog-claw-setup.tsx -->
Expand All @@ -143,8 +145,6 @@
<!-- packages/opencode/src/cli/cmd/providers.ts -->
- <https://opencode.ai/go>
<!-- packages/opencode/src/cli/cmd/tui/component/dialog-retry-action.tsx -->
- <https://opencode.ai/install>
<!-- packages/opencode/src/installation/index.ts -->
- <https://opencode.ai/tui.json>
<!-- packages/opencode/src/cli/cmd/tui/config/tui-migrate.ts -->
- <https://opencode.ai/zen>
Expand Down
5 changes: 3 additions & 2 deletions packages/opencode/src/cli/cmd/uninstall.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import path from "path"
import os from "os"
import { Filesystem } from "@/util/filesystem"
import { Process } from "@/util/process"
import { Brew as KiloBrew } from "@/kilocode/installation" // kilocode_change

interface UninstallArgs {
keepConfig: boolean
Expand Down Expand Up @@ -133,7 +134,7 @@ async function showRemovalSummary(targets: RemovalTargets, method: Installation.
pnpm: "pnpm uninstall -g @kilocode/cli", // kilocode_change
bun: "bun remove -g @kilocode/cli", // kilocode_change
yarn: "yarn global remove @kilocode/cli", // kilocode_change
brew: "brew uninstall opencode",
brew: `brew uninstall ${KiloBrew.name}`, // kilocode_change
choco: "choco uninstall kilo", // kilocode_change
scoop: "scoop uninstall kilo", // kilocode_change
}
Expand Down Expand Up @@ -184,7 +185,7 @@ async function executeUninstall(method: Installation.Method, targets: RemovalTar
pnpm: ["pnpm", "uninstall", "-g", "@kilocode/cli"], // kilocode_change
bun: ["bun", "remove", "-g", "@kilocode/cli"], // kilocode_change
yarn: ["yarn", "global", "remove", "@kilocode/cli"], // kilocode_change
brew: ["brew", "uninstall", "opencode"],
brew: ["brew", "uninstall", KiloBrew.name], // kilocode_change
choco: ["choco", "uninstall", "kilo"], // kilocode_change
scoop: ["scoop", "uninstall", "kilo"], // kilocode_change
}
Expand Down
4 changes: 2 additions & 2 deletions packages/opencode/src/cli/cmd/upgrade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const UpgradeCommand = {
alias: "m",
describe: "installation method to use",
type: "string",
choices: ["curl", "npm", "pnpm", "bun", "brew", "choco", "scoop"],
choices: ["curl", "npm", "yarn", "pnpm", "bun", "brew", "choco", "scoop"], // kilocode_change
})
},
handler: async (args: { target?: string; method?: string }) => {
Expand All @@ -43,7 +43,7 @@ export const UpgradeCommand = {
}
}
prompts.log.info("Using method: " + method)
const target = args.target ? args.target.replace(/^v/, "") : await Installation.latest()
const target = args.target ? args.target.replace(/^v/, "") : await Installation.latest(method) // kilocode_change

if (InstallationVersion === target) {
prompts.log.warn(`kilo upgrade skipped: ${target} is already installed`) // kilocode_change
Expand Down
4 changes: 2 additions & 2 deletions packages/opencode/src/cli/upgrade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ export async function upgrade() {
const config = await AppRuntime.runPromise(Config.Service.use((cfg) => cfg.getGlobal()))
if (config.autoupdate === false || Flag.KILO_DISABLE_AUTOUPDATE) return
const method = await Installation.method()
// kilocode_change start - only auto-upgrade for npm/pnpm/bun (we only publish @kilocode/cli via npm registry)
if (method !== "npm" && method !== "pnpm" && method !== "bun") return
// kilocode_change start - only auto-upgrade for npm/yarn/pnpm/bun (we only publish @kilocode/cli via npm registry)
if (method !== "npm" && method !== "yarn" && method !== "pnpm" && method !== "bun") return
// kilocode_change end
const latest = await Installation.latest(method).catch(() => {})
if (!latest) return
Expand Down
69 changes: 41 additions & 28 deletions packages/opencode/src/installation/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import { makeRuntime } from "@opencode-ai/core/effect/runtime"
import semver from "semver"
import { InstallationChannel, InstallationVersion } from "@opencode-ai/core/installation/version"
import { NpmConfig } from "@opencode-ai/core/npm-config"
// kilocode_change start
import { Brew as KiloBrew, Choco as KiloChoco, Npm as KiloNpm, Release as KiloRelease, Scoop as KiloScoop } from "@/kilocode/installation"
// kilocode_change end

const log = Log.create({ service: "installation" })

Expand Down Expand Up @@ -51,7 +54,7 @@ export const Info = Schema.Struct({
export type Info = Schema.Schema.Type<typeof Info>

export function userAgent(client = "cli") {
return `opencode/${InstallationChannel}/${InstallationVersion}/${client}`
return `kilo/${InstallationChannel}/${InstallationVersion}/${client}` // kilocode_change
}

export const USER_AGENT = userAgent()
Expand Down Expand Up @@ -129,15 +132,15 @@ export const layer: Layer.Layer<Service, never, HttpClient.HttpClient | AppProce
)

const getBrewFormula = Effect.fnUntraced(function* () {
const tapFormula = yield* text(["brew", "list", "--formula", "anomalyco/tap/opencode"])
if (tapFormula.includes("opencode")) return "anomalyco/tap/opencode"
const coreFormula = yield* text(["brew", "list", "--formula", "opencode"])
if (coreFormula.includes("opencode")) return "opencode"
return "opencode"
const tapFormula = yield* text(["brew", "list", "--formula", KiloBrew.formula]) // kilocode_change
if (tapFormula.includes(KiloBrew.name)) return KiloBrew.formula // kilocode_change
const coreFormula = yield* text(["brew", "list", "--formula", KiloBrew.name]) // kilocode_change
if (coreFormula.includes(KiloBrew.name)) return KiloBrew.name // kilocode_change
return KiloBrew.formula // kilocode_change
})

const upgradeCurl = Effect.fnUntraced(function* (target: string) {
const response = yield* httpOk.execute(HttpClientRequest.get("https://opencode.ai/install"))
const response = yield* httpOk.execute(HttpClientRequest.get(KiloRelease.install)) // kilocode_change
const body = yield* response.text
const bodyBytes = new TextEncoder().encode(body)
const result = yield* appProcess.run(
Expand All @@ -162,6 +165,7 @@ export const layer: Layer.Layer<Service, never, HttpClient.HttpClient | AppProce
}
}),
method: Effect.fn("Installation.method")(function* () {
if (process.execPath.includes(path.join(".kilo", "bin"))) return "curl" as Method // kilocode_change
if (process.execPath.includes(path.join(".opencode", "bin"))) return "curl" as Method
if (process.execPath.includes(path.join(".local", "bin"))) return "curl" as Method
const exec = process.execPath.toLowerCase()
Expand All @@ -171,9 +175,9 @@ export const layer: Layer.Layer<Service, never, HttpClient.HttpClient | AppProce
{ name: "yarn", command: () => text(["yarn", "global", "list"]) },
{ name: "pnpm", command: () => text(["pnpm", "list", "-g", "--depth=0"]) },
{ name: "bun", command: () => text(["bun", "pm", "ls", "-g"]) },
{ name: "brew", command: () => text(["brew", "list", "--formula", "opencode"]) },
{ name: "scoop", command: () => text(["scoop", "list", "opencode"]) },
{ name: "choco", command: () => text(["choco", "list", "--limit-output", "opencode"]) },
{ name: "brew", command: () => text(["brew", "list", "--formula", KiloBrew.formula]) }, // kilocode_change
{ name: "scoop", command: () => text(["scoop", "list", KiloScoop.name]) }, // kilocode_change
{ name: "choco", command: () => text(["choco", "list", "--limit-output", KiloChoco.name]) }, // kilocode_change
]

checks.sort((a, b) => {
Expand All @@ -186,8 +190,16 @@ export const layer: Layer.Layer<Service, never, HttpClient.HttpClient | AppProce

for (const check of checks) {
const output = yield* check.command()
// kilocode_change start
const installedName =
check.name === "brew" || check.name === "choco" || check.name === "scoop" ? "opencode" : "opencode-ai"
check.name === "brew"
? KiloBrew.name
: check.name === "choco"
? KiloChoco.name
: check.name === "scoop"
? KiloScoop.name
: KiloNpm.name
// kilocode_change end
if (output.includes(installedName)) {
return check.name
}
Expand All @@ -206,18 +218,16 @@ export const layer: Layer.Layer<Service, never, HttpClient.HttpClient | AppProce
return info.formulae[0].versions.stable
}
const response = yield* httpOk.execute(
HttpClientRequest.get("https://formulae.brew.sh/api/formula/opencode.json").pipe(
HttpClientRequest.acceptJson,
),
HttpClientRequest.get(KiloBrew.api).pipe(HttpClientRequest.acceptJson), // kilocode_change
)
const data = yield* HttpClientResponse.schemaBodyJson(BrewFormula)(response)
return data.versions.stable
}

if (detectedMethod === "npm" || detectedMethod === "bun" || detectedMethod === "pnpm") {
if (detectedMethod === "npm" || detectedMethod === "yarn" || detectedMethod === "bun" || detectedMethod === "pnpm") { // kilocode_change
const response = yield* httpOk.execute(
HttpClientRequest.get(
`${yield* NpmConfig.registry(process.cwd())}/opencode-ai/${InstallationChannel}`,
`${yield* NpmConfig.registry(process.cwd())}/${KiloNpm.path}/${InstallationChannel}`, // kilocode_change
).pipe(HttpClientRequest.acceptJson),
)
const data = yield* HttpClientResponse.schemaBodyJson(NpmPackage)(response)
Expand All @@ -227,7 +237,7 @@ export const layer: Layer.Layer<Service, never, HttpClient.HttpClient | AppProce
if (detectedMethod === "choco") {
const response = yield* httpOk.execute(
HttpClientRequest.get(
"https://community.chocolatey.org/api/v2/Packages?$filter=Id%20eq%20%27opencode%27%20and%20IsLatestVersion&$select=Version",
KiloChoco.api, // kilocode_change
).pipe(HttpClientRequest.setHeaders({ Accept: "application/json;odata=verbose" })),
)
const data = yield* HttpClientResponse.schemaBodyJson(ChocoPackage)(response)
Expand All @@ -237,17 +247,15 @@ export const layer: Layer.Layer<Service, never, HttpClient.HttpClient | AppProce
if (detectedMethod === "scoop") {
const response = yield* httpOk.execute(
HttpClientRequest.get(
"https://raw.githubusercontent.com/ScoopInstaller/Main/master/bucket/opencode.json",
KiloScoop.manifest, // kilocode_change
).pipe(HttpClientRequest.setHeaders({ Accept: "application/json" })),
)
const data = yield* HttpClientResponse.schemaBodyJson(ScoopManifest)(response)
return data.version
}

const response = yield* httpOk.execute(
HttpClientRequest.get("https://api.github.com/repos/anomalyco/opencode/releases/latest").pipe(
HttpClientRequest.acceptJson,
),
HttpClientRequest.get(KiloRelease.api).pipe(HttpClientRequest.acceptJson), // kilocode_change
)
const data = yield* HttpClientResponse.schemaBodyJson(GitHubRelease)(response)
return data.tag_name.replace(/^v/, "")
Expand All @@ -258,25 +266,30 @@ export const layer: Layer.Layer<Service, never, HttpClient.HttpClient | AppProce
case "curl":
upgradeResult = yield* upgradeCurl(target)
break
// kilocode_change start
case "npm":
upgradeResult = yield* run(["npm", "install", "-g", `opencode-ai@${target}`])
upgradeResult = yield* run(["npm", "install", "-g", `${KiloNpm.name}@${target}`])
break
case "yarn":
upgradeResult = yield* run(["yarn", "global", "add", `${KiloNpm.name}@${target}`])
break
// kilocode_change end
case "pnpm":
upgradeResult = yield* run(["pnpm", "install", "-g", `opencode-ai@${target}`])
upgradeResult = yield* run(["pnpm", "install", "-g", `${KiloNpm.name}@${target}`]) // kilocode_change
break
case "bun":
upgradeResult = yield* run(["bun", "install", "-g", `opencode-ai@${target}`])
upgradeResult = yield* run(["bun", "install", "-g", `${KiloNpm.name}@${target}`]) // kilocode_change
break
case "brew": {
const formula = yield* getBrewFormula()
const env = { HOMEBREW_NO_AUTO_UPDATE: "1" }
if (formula.includes("/")) {
const tap = yield* run(["brew", "tap", "anomalyco/tap"], { env })
const tap = yield* run(["brew", "tap", KiloBrew.tap], { env }) // kilocode_change
if (tap.code !== 0) {
upgradeResult = tap
break
}
const repo = yield* text(["brew", "--repo", "anomalyco/tap"])
const repo = yield* text(["brew", "--repo", KiloBrew.tap]) // kilocode_change
const dir = repo.trim()
if (dir) {
const pull = yield* run(["git", "pull", "--ff-only"], { cwd: dir, env })
Expand All @@ -290,10 +303,10 @@ export const layer: Layer.Layer<Service, never, HttpClient.HttpClient | AppProce
break
}
case "choco":
upgradeResult = yield* run(["choco", "upgrade", "opencode", `--version=${target}`, "-y"])
upgradeResult = yield* run(["choco", "upgrade", KiloChoco.name, `--version=${target}`, "-y"]) // kilocode_change
break
case "scoop":
upgradeResult = yield* run(["scoop", "install", `opencode@${target}`])
upgradeResult = yield* run(["scoop", "install", `${KiloScoop.name}@${target}`]) // kilocode_change
break
default:
return yield* new UpgradeFailedError({ stderr: `Unknown method: ${m}` })
Expand Down
26 changes: 26 additions & 0 deletions packages/opencode/src/kilocode/installation/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export const Npm = {
name: "@kilocode/cli",
path: "@kilocode%2fcli",
}

export const Brew = {
name: "kilo",
tap: "Kilo-Org/tap",
formula: "Kilo-Org/tap/kilo",
api: "https://formulae.brew.sh/api/formula/kilo.json",
}

export const Choco = {
name: "kilo",
api: "https://community.chocolatey.org/api/v2/Packages?$filter=Id%20eq%20%27kilo%27%20and%20IsLatestVersion&$select=Version",
}

export const Scoop = {
name: "kilo",
manifest: "https://raw.githubusercontent.com/ScoopInstaller/Main/master/bucket/kilo.json",
}

export const Release = {
api: "https://api.github.com/repos/Kilo-Org/kilocode/releases/latest",
install: "https://kilo.ai/install",
}
12 changes: 6 additions & 6 deletions packages/opencode/test/installation/installation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ describe("installation", () => {
Effect.gen(function* () {
const result = yield* Installation.Service.use((svc) => svc.latest("npm"))
expect(result).toBe("1.5.0")
expect(npmCalls).toContain(`https://registry.npmjs.org/opencode-ai/${InstallationChannel}`)
expect(npmCalls).toContain(`https://registry.npmjs.org/@kilocode%2fcli/${InstallationChannel}`) // kilocode_change
}),
)

Expand All @@ -96,7 +96,7 @@ describe("installation", () => {
Effect.gen(function* () {
const result = yield* Installation.Service.use((svc) => svc.latest("bun"))
expect(result).toBe("1.6.0")
expect(bunCalls).toContain(`https://registry.npmjs.org/opencode-ai/${InstallationChannel}`)
expect(bunCalls).toContain(`https://registry.npmjs.org/@kilocode%2fcli/${InstallationChannel}`) // kilocode_change
}),
)

Expand All @@ -110,7 +110,7 @@ describe("installation", () => {
Effect.gen(function* () {
const result = yield* Installation.Service.use((svc) => svc.latest("pnpm"))
expect(result).toBe("1.7.0")
expect(pnpmCalls).toContain(`https://registry.npmjs.org/opencode-ai/${InstallationChannel}`)
expect(pnpmCalls).toContain(`https://registry.npmjs.org/@kilocode%2fcli/${InstallationChannel}`) // kilocode_change
}),
)

Expand All @@ -135,8 +135,8 @@ describe("installation", () => {
() => jsonResponse({ versions: { stable: "2.0.0" } }),
(cmd, args) => {
// getBrewFormula: return core formula (no tap)
if (cmd === "brew" && args.includes("--formula") && args.includes("anomalyco/tap/opencode")) return ""
if (cmd === "brew" && args.includes("--formula") && args.includes("opencode")) return "opencode"
if (cmd === "brew" && args.includes("--formula") && args.includes("Kilo-Org/tap/kilo")) return "" // kilocode_change
if (cmd === "brew" && args.includes("--formula") && args.includes("kilo")) return "kilo" // kilocode_change
return ""
},
),
Expand All @@ -154,7 +154,7 @@ describe("installation", () => {
testLayer(
() => jsonResponse({}), // HTTP not used for tap formula
(cmd, args) => {
if (cmd === "brew" && args.includes("anomalyco/tap/opencode") && args.includes("--formula")) return "opencode"
if (cmd === "brew" && args.includes("Kilo-Org/tap/kilo") && args.includes("--formula")) return "kilo" // kilocode_change
if (cmd === "brew" && args.includes("--json=v2")) return brewInfoJson
return ""
},
Expand Down
Loading
Loading