fix(cli): skip startup work for informational commands

This commit is contained in:
Mohammad Javad Naderi
2026-07-29 18:50:20 +03:30
parent bba2dba0a8
commit 39f65efa8b
5 changed files with 75 additions and 3 deletions
+6
View File
@@ -0,0 +1,6 @@
---
"@kilocode/cli": patch
"@kilocode/kilo-telemetry": patch
---
Skip API and telemetry lifecycle work for informational CLI commands and avoid profile requests when telemetry is disabled.
@@ -85,6 +85,19 @@ describe("TelemetryEvent", () => {
})
describe("Telemetry", () => {
test("skips identity updates when disabled", async () => {
const enabled = spyOn(Client, "isEnabled").mockReturnValue(false)
const update = spyOn(Identity, "updateFromKiloAuth").mockResolvedValue()
try {
await Telemetry.updateIdentity("token")
expect(update).not.toHaveBeenCalled()
} finally {
enabled.mockRestore()
update.mockRestore()
}
})
test("includes host OS properties", () => {
const capture = spyOn(Client, "capture").mockImplementation(() => {})
+2
View File
@@ -105,6 +105,8 @@ export namespace Telemetry {
}
export async function updateIdentity(token: string | null, accountId?: string): Promise<void> {
if (!isEnabled()) return
const previousId = Identity.getDistinctId()
await Identity.updateFromKiloAuth(token, accountId)
@@ -46,6 +46,13 @@ KiloShutdown.register(async () => {
// (src/index.ts) only needs a handful of thin call-sites behind kilocode_change markers.
// This keeps index.ts close to upstream and reduces merge conflicts on every sync.
export namespace KiloCli {
function informational() {
const args = process.argv.slice(2)
const end = args.indexOf("--")
const flags = ["--help", "-h", "--version", "-v"]
return args.slice(0, end === -1 ? args.length : end).some((arg) => flags.includes(arg))
}
// Register only the Kilo-specific commands. Upstream commands stay in index.ts's chain so
// upstream merges that add or remove commands keep working without touching this file.
export function register<T>(cli: Argv<T>): Argv<T> {
@@ -73,6 +80,8 @@ export namespace KiloCli {
// Runs from the upstream `.middleware`, before any command handler. Env tagging is additive so
// it never has to modify upstream's own env assignments.
export async function bootstrap(): Promise<void> {
if (informational()) return
await KiloLog.init()
if (!process.env[ENV_FEATURE]) process.env[ENV_FEATURE] = process.argv.includes("serve") ? "unknown" : "cli"
if (!process.env[ENV_VERSION]) process.env[ENV_VERSION] = InstallationVersion
@@ -107,6 +116,8 @@ export namespace KiloCli {
// Runs from the `finally` block on every exit path.
export async function shutdown(): Promise<void> {
if (informational()) return
const code = typeof process.exitCode === "number" ? process.exitCode : undefined
Telemetry.trackCliExit(code)
try {
@@ -19,7 +19,9 @@ mock.module("@opencode-ai/core/installation/version", () => ({
mock.module("@kilocode/kilo-telemetry", () => ({
Telemetry: {
async init() {},
async init() {
calls.push("telemetry:init")
},
async updateIdentity() {},
trackCliStart() {},
trackCliExit(code?: number) {
@@ -36,16 +38,36 @@ mock.module("@kilocode/kilo-telemetry", () => ({
mock.module("@kilocode/kilo-gateway", () => ({
ENV_FEATURE: "KILO_FEATURE",
ENV_VERSION: "KILO_VERSION",
async migrateLegacyKiloAuth() {},
async migrateLegacyKiloAuth() {
calls.push("auth:migrate")
},
}))
mock.module("@/effect/app-runtime", () => ({
AppRuntime: {
async runPromise() {},
async runPromise() {
calls.push("runtime")
},
async dispose() {},
},
}))
mock.module("@/kilocode/log", () => ({
KiloLog: {
async init() {
calls.push("log")
},
},
}))
mock.module("@/kilocode/storage/json-migration", () => ({
JsonMigration: {
async bootstrap() {
calls.push("migration")
},
},
}))
mock.module("@/config/config", () => ({
Config: { Service: { use: () => ({ experimental: {} }) } },
}))
@@ -183,4 +205,22 @@ describe("KiloCli.shutdown", () => {
expect(calls).toEqual(["track:1", "session", "telemetry", "drain", "dispose"])
expect(process.exitCode).toBe(1)
})
test("skips lifecycle work for informational flags", async () => {
const { KiloCli } = await import("../../src/kilocode/cli/setup")
await installDrain()
for (const flag of ["--help", "--version"]) {
process.argv.push(flag)
try {
await KiloCli.bootstrap()
await KiloCli.shutdown()
} finally {
process.argv.pop()
}
}
expect(calls).toEqual([])
expect(timeouts).toEqual([])
})
})