From 3185e8d58285e15b4638c5ec61612b1f4a4f140e Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Mon, 18 May 2026 15:25:16 +0200 Subject: [PATCH] fix: rebrand upstream attribution and bug-report URLs Replaces hardcoded opencode.ai/anomalyco URLs in production code paths: LLM provider HTTP-Referer/X-Title attribution headers, the TUI error dialog's bug-report URL, and the github-remote parser test fixtures. Extends check-forbidden-strings with patterns for these leaks (with a narrow allowlist for fork-lineage docs and upstream-merge tooling). --- .../cli/cmd/tui/component/error-component.tsx | 2 +- packages/opencode/src/provider/provider.ts | 28 ++++++------- .../opencode/test/cli/github-remote.test.ts | 20 ++++++--- script/check-forbidden-strings.ts | 41 ++++++++++++++++--- 4 files changed, 65 insertions(+), 26 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/component/error-component.tsx b/packages/opencode/src/cli/cmd/tui/component/error-component.tsx index fcbd27ca9bd..8d7fe968802 100644 --- a/packages/opencode/src/cli/cmd/tui/component/error-component.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/error-component.tsx @@ -31,7 +31,7 @@ export function ErrorComponent(props: { }) const [copied, setCopied] = createSignal(false) - const issueURL = new URL("https://github.com/anomalyco/opencode/issues/new?template=bug-report.yml") + const issueURL = new URL("https://github.com/Kilo-Org/kilocode/issues/new?template=bug-report.yml") // kilocode_change // Choose safe fallback colors per mode since theme context may not be available const isLight = props.mode === "light" diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index 24c54c3663d..7b77f2742d1 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -440,9 +440,9 @@ function custom(dep: CustomDep): Record { autoload: false, options: { headers: { - "HTTP-Referer": "https://opencode.ai/", - "X-Title": "opencode", - "X-Source": "opencode", + "HTTP-Referer": "https://kilo.ai/", // kilocode_change + "X-Title": "Kilo Code", // kilocode_change + "X-Source": "kilo", // kilocode_change }, }, }), @@ -451,8 +451,8 @@ function custom(dep: CustomDep): Record { autoload: false, options: { headers: { - "HTTP-Referer": "https://opencode.ai/", - "X-Title": "opencode", + "HTTP-Referer": "https://kilo.ai/", // kilocode_change + "X-Title": "Kilo Code", // kilocode_change }, }, }), @@ -461,8 +461,8 @@ function custom(dep: CustomDep): Record { autoload: false, options: { headers: { - "HTTP-Referer": "https://opencode.ai/", - "X-Title": "opencode", + "HTTP-Referer": "https://kilo.ai/", // kilocode_change + "X-Title": "Kilo Code", // kilocode_change "X-BILLING-INVOKE-ORIGIN": "KiloCode", // kilocode_change }, }, @@ -472,8 +472,8 @@ function custom(dep: CustomDep): Record { autoload: false, options: { headers: { - "http-referer": "https://opencode.ai/", - "x-title": "opencode", + "http-referer": "https://kilo.ai/", // kilocode_change + "x-title": "Kilo Code", // kilocode_change }, }, }), @@ -570,8 +570,8 @@ function custom(dep: CustomDep): Record { autoload: false, options: { headers: { - "HTTP-Referer": "https://opencode.ai/", - "X-Title": "opencode", + "HTTP-Referer": "https://kilo.ai/", // kilocode_change + "X-Title": "Kilo Code", // kilocode_change }, }, }), @@ -846,7 +846,7 @@ function custom(dep: CustomDep): Record { autoload: false, options: { headers: { - "X-Cerebras-3rd-Party-Integration": "opencode", + "X-Cerebras-3rd-Party-Integration": "Kilo Code", // kilocode_change }, }, }), @@ -855,8 +855,8 @@ function custom(dep: CustomDep): Record { autoload: false, options: { headers: { - "HTTP-Referer": "https://opencode.ai/", - "X-Title": "opencode", + "HTTP-Referer": "https://kilo.ai/", // kilocode_change + "X-Title": "Kilo Code", // kilocode_change }, }, }), diff --git a/packages/opencode/test/cli/github-remote.test.ts b/packages/opencode/test/cli/github-remote.test.ts index 80102d986ea..89a7c8f27a3 100644 --- a/packages/opencode/test/cli/github-remote.test.ts +++ b/packages/opencode/test/cli/github-remote.test.ts @@ -1,29 +1,37 @@ import { test, expect } from "bun:test" import { parseGitHubRemote } from "../../src/cli/cmd/github" +// kilocode_change start: rebrand fixtures off upstream repo path test("parses https URL with .git suffix", () => { - expect(parseGitHubRemote("https://github.com/sst/opencode.git")).toEqual({ owner: "sst", repo: "opencode" }) + expect(parseGitHubRemote("https://github.com/Kilo-Org/kilocode.git")).toEqual({ + owner: "Kilo-Org", + repo: "kilocode", + }) }) test("parses https URL without .git suffix", () => { - expect(parseGitHubRemote("https://github.com/sst/opencode")).toEqual({ owner: "sst", repo: "opencode" }) + expect(parseGitHubRemote("https://github.com/Kilo-Org/kilocode")).toEqual({ owner: "Kilo-Org", repo: "kilocode" }) }) test("parses git@ URL with .git suffix", () => { - expect(parseGitHubRemote("git@github.com:sst/opencode.git")).toEqual({ owner: "sst", repo: "opencode" }) + expect(parseGitHubRemote("git@github.com:Kilo-Org/kilocode.git")).toEqual({ owner: "Kilo-Org", repo: "kilocode" }) }) test("parses git@ URL without .git suffix", () => { - expect(parseGitHubRemote("git@github.com:sst/opencode")).toEqual({ owner: "sst", repo: "opencode" }) + expect(parseGitHubRemote("git@github.com:Kilo-Org/kilocode")).toEqual({ owner: "Kilo-Org", repo: "kilocode" }) }) test("parses ssh:// URL with .git suffix", () => { - expect(parseGitHubRemote("ssh://git@github.com/sst/opencode.git")).toEqual({ owner: "sst", repo: "opencode" }) + expect(parseGitHubRemote("ssh://git@github.com/Kilo-Org/kilocode.git")).toEqual({ + owner: "Kilo-Org", + repo: "kilocode", + }) }) test("parses ssh:// URL without .git suffix", () => { - expect(parseGitHubRemote("ssh://git@github.com/sst/opencode")).toEqual({ owner: "sst", repo: "opencode" }) + expect(parseGitHubRemote("ssh://git@github.com/Kilo-Org/kilocode")).toEqual({ owner: "Kilo-Org", repo: "kilocode" }) }) +// kilocode_change end test("parses http URL", () => { expect(parseGitHubRemote("http://github.com/owner/repo")).toEqual({ owner: "owner", repo: "repo" }) diff --git a/script/check-forbidden-strings.ts b/script/check-forbidden-strings.ts index aecd66a8d10..a88cabf42ae 100644 --- a/script/check-forbidden-strings.ts +++ b/script/check-forbidden-strings.ts @@ -4,10 +4,10 @@ /** * Greps tracked files for forbidden strings that must not appear in the repo. * - * Currently enforced: - * - opncd.ai/s/ -- legacy upstream OpenCode share URL pattern. Kilo shares - * go through a different host/path; this string sneaking - * back in usually means a hardcoded upstream URL. + * Each entry is a literal substring (no regex / globs) plus a one-line reason. + * If a hit is genuinely legitimate (e.g. inside upstream-merge tooling), fix the + * call site rather than weakening the rule -- the list is intentionally + * narrow so it stays low-noise. */ import { spawnSync } from "node:child_process" @@ -16,7 +16,37 @@ import path from "node:path" const ROOT = path.resolve(import.meta.dir, "..") const SELF = path.relative(ROOT, import.meta.path).replaceAll("\\", "/") -const forbidden = [{ pattern: "opncd.ai/s/", reason: "legacy upstream share URL pattern" }] +// Each entry: pattern (literal substring) + reason + optional allow list of path +// prefixes where the string is legitimate (e.g. docs describing the fork lineage, +// upstream-merge tooling, generated source-link manifests). +const forbidden: { pattern: string; reason: string; allow?: string[] }[] = [ + { pattern: "opncd.ai/s/", reason: "legacy upstream share URL pattern" }, + { + pattern: "github.com/anomalyco/opencode", + reason: "upstream repo URL -- should be Kilo-Org/kilocode", + allow: [ + "AGENTS.md", + "README.md", + ".opencode/glossary/", + "packages/kilo-vscode/AGENTS.md", + "packages/kilo-docs/source-links.md", + "patches/", + "script/upstream/", + ], + }, + { + pattern: "sst/opencode", + reason: "old upstream org path -- should be Kilo-Org/kilocode", + allow: [".kilo/agent/upstream-merge.md", "script/upstream/"], + }, + { pattern: `"HTTP-Referer": "https://opencode.ai/"`, reason: "attributes outbound LLM traffic to upstream" }, + { pattern: `"http-referer": "https://opencode.ai/"`, reason: "attributes outbound LLM traffic to upstream" }, +] + +const isAllowed = (file: string, allow?: string[]) => { + if (!allow) return false + return allow.some((prefix) => file === prefix || file.startsWith(prefix)) +} const ls = spawnSync("git", ["ls-files", "-z"], { cwd: ROOT, encoding: "buffer" }) if (ls.status !== 0) { @@ -39,6 +69,7 @@ for (const file of files) { if (text === null) continue if (text.includes("\0")) continue for (const f of forbidden) { + if (isAllowed(file, f.allow)) continue let idx = 0 while (true) { const at = text.indexOf(f.pattern, idx)