From a2d18131a8192b47591af0885fad8562298b69de Mon Sep 17 00:00:00 2001 From: Evgeny Shurakov Date: Wed, 18 Feb 2026 22:38:46 +0100 Subject: [PATCH 1/4] feat(kilo-sessions): send git remote URL and branch to ingest Add gitUrl and gitBranch fields to kilo_meta so shared sessions include repository context. Resolves the best remote (origin > single available > upstream) and fetches the URL with a 10s in-flight cache. --- .../src/kilo-sessions/ingest-queue.ts | 2 + .../src/kilo-sessions/kilo-sessions.ts | 47 +++++++++++++++++++ .../test/kilo-sessions/ingest-queue.test.ts | 13 ++++- 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/packages/opencode/src/kilo-sessions/ingest-queue.ts b/packages/opencode/src/kilo-sessions/ingest-queue.ts index fefcd45327..1ec105ea4a 100644 --- a/packages/opencode/src/kilo-sessions/ingest-queue.ts +++ b/packages/opencode/src/kilo-sessions/ingest-queue.ts @@ -16,6 +16,8 @@ export namespace IngestQueue { data: { platform: string orgId?: string + gitUrl?: string + gitBranch?: string } } | { diff --git a/packages/opencode/src/kilo-sessions/kilo-sessions.ts b/packages/opencode/src/kilo-sessions/kilo-sessions.ts index 5f00adb63f..04cc2803cf 100644 --- a/packages/opencode/src/kilo-sessions/kilo-sessions.ts +++ b/packages/opencode/src/kilo-sessions/kilo-sessions.ts @@ -10,6 +10,9 @@ import { clearInFlightCache, withInFlightCache } from "@/kilo-sessions/inflight- import type * as SDK from "@kilocode/sdk/v2" import z from "zod" import { KILO_API_BASE } from "@kilocode/kilo-gateway" +import { $ } from "bun" +import { Instance } from "@/project/instance" +import { Vcs } from "@/project/vcs" export namespace KiloSessions { const log = Log.create({ service: "kilo-sessions" }) @@ -23,6 +26,7 @@ export namespace KiloSessions { const tokenKey = "kilo-sessions:token" const orgKey = "kilo-sessions:org" const clientKey = "kilo-sessions:client" + const gitUrlKey = "kilo-sessions:git-url" const ttlMs = 10_000 @@ -31,6 +35,7 @@ export namespace KiloSessions { clearInFlightCache(tokenValidKey) clearInFlightCache(clientKey) clearInFlightCache(orgKey) + clearInFlightCache(gitUrlKey) } async function authValid(token: string) { @@ -370,13 +375,55 @@ export namespace KiloSessions { ]) } + async function getGitUrl(): Promise { + return withInFlightCache(gitUrlKey, ttlMs, async () => { + const result = await $`git remote` + .quiet() + .nothrow() + .cwd(Instance.worktree) + .text() + .catch(() => "") + + const remotes = result + .split("\n") + .map((line) => line.trim()) + .filter(Boolean) + + if (remotes.length === 0) return undefined + + const remote = remotes.includes("origin") + ? "origin" + : remotes.length === 1 + ? remotes[0] + : remotes.includes("upstream") + ? "upstream" + : undefined + + if (!remote) return undefined + + const url = await $`git config --get remote.${remote}.url` + .quiet() + .nothrow() + .cwd(Instance.worktree) + .text() + .then((x) => x.trim()) + .catch(() => "") + + return url || undefined + }) + } + async function meta() { const platform = process.env["KILO_PLATFORM"] || "cli" const orgId = await getOrgId() + const gitBranch = await Vcs.branch().catch(() => undefined) + const gitUrl = await getGitUrl().catch(() => undefined) return { platform, ...(orgId ? { orgId } : {}), + ...(gitUrl ? { gitUrl } : {}), + ...(gitBranch ? { gitBranch } : {}), } } diff --git a/packages/opencode/test/kilo-sessions/ingest-queue.test.ts b/packages/opencode/test/kilo-sessions/ingest-queue.test.ts index 7c09477179..529be44b21 100644 --- a/packages/opencode/test/kilo-sessions/ingest-queue.test.ts +++ b/packages/opencode/test/kilo-sessions/ingest-queue.test.ts @@ -137,9 +137,16 @@ describe("share ingest queue", () => { }), }) - await q.sync("s7", [{ type: "kilo_meta", data: { platform: "cli" } }]) + await q.sync("s7", [ + { type: "kilo_meta", data: { platform: "cli", gitUrl: "https://github.com/old/repo.git", gitBranch: "main" } }, + ]) clock.now = 100 - await q.sync("s7", [{ type: "kilo_meta", data: { platform: "vscode", orgId: "org-1" } }]) + await q.sync("s7", [ + { + type: "kilo_meta", + data: { platform: "vscode", orgId: "org-1", gitUrl: "https://github.com/new/repo.git", gitBranch: "feature" }, + }, + ]) clock.now = 1000 sched.run() @@ -149,6 +156,8 @@ describe("share ingest queue", () => { expect((sent[0] as any).data[0].type).toBe("kilo_meta") expect((sent[0] as any).data[0].data.platform).toBe("vscode") expect((sent[0] as any).data[0].data.orgId).toBe("org-1") + expect((sent[0] as any).data[0].data.gitUrl).toBe("https://github.com/new/repo.git") + expect((sent[0] as any).data[0].data.gitBranch).toBe("feature") }) test("network failure retries and fill preserves newer updates", async () => { From 872cb4124b05cc4adc09e14f912a697cff11849c Mon Sep 17 00:00:00 2001 From: Evgeny Shurakov Date: Wed, 18 Feb 2026 22:47:46 +0100 Subject: [PATCH 2/4] feat(kilo-sessions): strip credentials from git remote URLs before ingesting --- .../opencode/src/kilo-sessions/kilo-sessions.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/kilo-sessions/kilo-sessions.ts b/packages/opencode/src/kilo-sessions/kilo-sessions.ts index 04cc2803cf..4450fee59b 100644 --- a/packages/opencode/src/kilo-sessions/kilo-sessions.ts +++ b/packages/opencode/src/kilo-sessions/kilo-sessions.ts @@ -375,6 +375,19 @@ export namespace KiloSessions { ]) } + /** Strip credentials from a git remote URL (e.g. https://user:token@github.com/org/repo.git) */ + function sanitizeGitUrl(raw: string): string { + if (!raw.startsWith("http://") && !raw.startsWith("https://")) return raw + try { + const parsed = new URL(raw) + parsed.username = "" + parsed.password = "" + return parsed.toString() + } catch { + return raw + } + } + async function getGitUrl(): Promise { return withInFlightCache(gitUrlKey, ttlMs, async () => { const result = await $`git remote` @@ -409,7 +422,7 @@ export namespace KiloSessions { .then((x) => x.trim()) .catch(() => "") - return url || undefined + return url ? sanitizeGitUrl(url) : undefined }) } From 69135ca63674340c821c848c82e8179e26c8c30b Mon Sep 17 00:00:00 2001 From: Evgeny Shurakov Date: Thu, 19 Feb 2026 12:11:38 +0100 Subject: [PATCH 3/4] feat(kilo-sessions): normalize git remote URLs to strip credentials, query params, and hash --- bun.lock | 1 + packages/opencode/package.json | 1 + .../src/kilo-sessions/kilo-sessions.ts | 46 +++++++------------ 3 files changed, 19 insertions(+), 29 deletions(-) diff --git a/bun.lock b/bun.lock index fd3415e85c..a36686a203 100644 --- a/bun.lock +++ b/bun.lock @@ -296,6 +296,7 @@ "opentui-spinner": "0.0.6", "partial-json": "0.1.7", "remeda": "catalog:", + "simple-git": "3.31.1", "solid-js": "catalog:", "strip-ansi": "7.1.2", "tree-sitter-bash": "0.25.0", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 950a080154..8c798073ff 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -112,6 +112,7 @@ "opentui-spinner": "0.0.6", "partial-json": "0.1.7", "remeda": "catalog:", + "simple-git": "3.31.1", "solid-js": "catalog:", "strip-ansi": "7.1.2", "tree-sitter-bash": "0.25.0", diff --git a/packages/opencode/src/kilo-sessions/kilo-sessions.ts b/packages/opencode/src/kilo-sessions/kilo-sessions.ts index 4450fee59b..1b32332909 100644 --- a/packages/opencode/src/kilo-sessions/kilo-sessions.ts +++ b/packages/opencode/src/kilo-sessions/kilo-sessions.ts @@ -10,9 +10,9 @@ import { clearInFlightCache, withInFlightCache } from "@/kilo-sessions/inflight- import type * as SDK from "@kilocode/sdk/v2" import z from "zod" import { KILO_API_BASE } from "@kilocode/kilo-gateway" -import { $ } from "bun" import { Instance } from "@/project/instance" import { Vcs } from "@/project/vcs" +import simpleGit from "simple-git" export namespace KiloSessions { const log = Log.create({ service: "kilo-sessions" }) @@ -375,54 +375,42 @@ export namespace KiloSessions { ]) } - /** Strip credentials from a git remote URL (e.g. https://user:token@github.com/org/repo.git) */ - function sanitizeGitUrl(raw: string): string { - if (!raw.startsWith("http://") && !raw.startsWith("https://")) return raw + /** Normalize a git remote URL: strip credentials, query params, and hash. Returns undefined for unrecognized formats. */ + function normalizeGitUrl(raw: string): string | undefined { + const ssh = raw.match(/^git@([^:]+):(.+)$/) + if (ssh) return `git@${ssh[1]}:${ssh[2].split("?")[0]}` try { const parsed = new URL(raw) + if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return undefined parsed.username = "" parsed.password = "" + parsed.search = "" + parsed.hash = "" return parsed.toString() } catch { - return raw + return undefined } } async function getGitUrl(): Promise { return withInFlightCache(gitUrlKey, ttlMs, async () => { - const result = await $`git remote` - .quiet() - .nothrow() - .cwd(Instance.worktree) - .text() - .catch(() => "") - - const remotes = result - .split("\n") - .map((line) => line.trim()) - .filter(Boolean) - + const repo = simpleGit(Instance.worktree) + const remotes = await repo.getRemotes(true).catch(() => []) if (remotes.length === 0) return undefined - const remote = remotes.includes("origin") + const names = remotes.map((r) => r.name) + const remote = names.includes("origin") ? "origin" : remotes.length === 1 - ? remotes[0] - : remotes.includes("upstream") + ? names[0] + : names.includes("upstream") ? "upstream" : undefined if (!remote) return undefined - const url = await $`git config --get remote.${remote}.url` - .quiet() - .nothrow() - .cwd(Instance.worktree) - .text() - .then((x) => x.trim()) - .catch(() => "") - - return url ? sanitizeGitUrl(url) : undefined + const url = remotes.find((r) => r.name === remote)?.refs.fetch ?? "" + return url ? normalizeGitUrl(url) : undefined }) } From 582dd685c008a2b25f5e8d7bbc75289f41288968 Mon Sep 17 00:00:00 2001 From: Evgeny Shurakov Date: Thu, 19 Feb 2026 13:48:37 +0100 Subject: [PATCH 4/4] fix(kilo-sessions): scope git URL cache key per worktree --- packages/opencode/src/kilo-sessions/kilo-sessions.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/opencode/src/kilo-sessions/kilo-sessions.ts b/packages/opencode/src/kilo-sessions/kilo-sessions.ts index 1b32332909..b6c8f59788 100644 --- a/packages/opencode/src/kilo-sessions/kilo-sessions.ts +++ b/packages/opencode/src/kilo-sessions/kilo-sessions.ts @@ -26,7 +26,7 @@ export namespace KiloSessions { const tokenKey = "kilo-sessions:token" const orgKey = "kilo-sessions:org" const clientKey = "kilo-sessions:client" - const gitUrlKey = "kilo-sessions:git-url" + const gitUrlKeyPrefix = "kilo-sessions:git-url:" const ttlMs = 10_000 @@ -35,7 +35,7 @@ export namespace KiloSessions { clearInFlightCache(tokenValidKey) clearInFlightCache(clientKey) clearInFlightCache(orgKey) - clearInFlightCache(gitUrlKey) + clearInFlightCache(gitUrlKeyPrefix + Instance.worktree) } async function authValid(token: string) { @@ -393,7 +393,7 @@ export namespace KiloSessions { } async function getGitUrl(): Promise { - return withInFlightCache(gitUrlKey, ttlMs, async () => { + return withInFlightCache(gitUrlKeyPrefix + Instance.worktree, ttlMs, async () => { const repo = simpleGit(Instance.worktree) const remotes = await repo.getRemotes(true).catch(() => []) if (remotes.length === 0) return undefined