Repository navigation
fix(prompt): rebrand every model-visible surface to CoDev Code - #165
Merged
Merged
Conversation
3 tasks done
Asked "who are you", the agent answered "I'm opencode" and sent users to upstream's issue tracker and docs. Only the UI had been rebranded; the system prompts the model actually receives came from upstream verbatim. Names are now rewritten as the prompt is assembled, by the fork-only packages/codev-brand, at the three points every model-visible string flows through: SystemPrompt.provider (upstream's branch chain renamed select() and left byte-identical), the tool registry's assembled description, and the built-in config skill. The prompt/*.txt sources and customize-opencode.md stay byte-identical to upstream, so their sync hunks keep applying and a new upstream brand mention gets rebranded on arrival instead of shipping unnoticed. Identity is also stated in the environment block, because an agent that defines its own `prompt` replaces the provider prompt wholesale and would otherwise have no idea what product it is. Identifiers that are not branding keep their upstream names: @opencode-ai/* packages, published plugin names such as opencode-gemini-auth, the config $schema URL, opencode://, ai.opencode.* and opencode.db. The built-in skill is renamed customize-opencode -> customize-codev; the old name still resolves so existing configs and permission rules keep working. test/session/brand.test.ts is the tripwire: it scans every embedded prompt and tool description and fails when a brand mention is not covered by the rules.
Every browser-opened docs link used base `netmind.viettel.vn/codev/docs`, but the docs site is locale-routed and the Code docs live under `/code`, so the real base is `/codev/en/docs/code`. The root only redirected (307) — the deep links did not: settings theme "Learn more" (v1 + v2), the custom-provider dialog link, and the CLI Cloudflare-gateway hint all 404'd. Verified against the live site: every new URL resolves 200, and the `#custom-provider` / `#cloudflare-ai-gateway` anchor ids exist on the new providers page. `codev-brand`'s DOCS_URL moves to the same base and is now base64-encoded via `atob`, matching codev-gateway/src/const.ts and core/src/ripgrep/binary.ts — host out of casual greps, not a secret. The ripgrep download mirror deliberately stays on `/codev/docs/code/downloads`: it is a `public/` static asset, not locale-routed content, and 404s under `/en/docs/code`. Moving it would brick the offline-rg fallback. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`netmind.viettel.vn` appeared in plaintext at 10 call sites across app, tui, and the CLI, which defeats the point of the base64 host in codev-gateway, core/ripgrep/binary.ts, and codev-brand. Rather than repeat an `atob(...)` at each site, every link now derives from `DOCS_URL` in `@codev/brand` — one encoded host in the codebase and one place to fix when the site moves. `app` and `tui` take a `@codev/brand` workspace dependency; it is a zero-dep leaf using only `atob`, so it is safe in client runtime code. The two tests that pinned a URL literal keep pinning it, base64-encoded, so they still verify the value independently of the constant they check. CHANGELOG and DIVERGENCES prose points at `DOCS_URL` / `MIRROR` instead of spelling out the host — same result as encoding, without turning the fork's conflict-resolution guidance into base64. DIVERGENCES gains a row for `packages/app/package.json` and extends the `packages/tui/package.json` row. Grep for netmind or viettel across the repo now returns nothing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Running prettier on DIVERGENCES.md realigns every row of the changed-files table, so a one-row edit produces a ~120-row diff — noise in the one file that records upstream-conflict guidance, and a guaranteed conflict on the next sync. Nothing in the repo formatted it before (the pre-push hook only runs `bun typecheck`), so this pins the status quo rather than changing it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Issue for this PR
Closes #
Type of change
What does this PR do?
Two bugs, both in text the model or the user's browser ends up pointed at.
1. The agent didn't know which product it is
Asked "who are you", the agent answered "I'm opencode", and it told users to report
issues at
github.com/anomalyco/opencodeand read the docs atopencode.ai.The rebrand so far covered the UI. It never covered the text the model actually reads:
SystemPrompt.provider()picks a prompt file by model id, and all 10 of those files areupstream's verbatim — 20 brand mentions across identity lines, the
/helpline, thefeedback URL and the docs URL. MiniMax-M3 matches no branch, so it lands on
default.txt,whose first line is literally
You are opencode, an interactive CLI tool….Names are now rewritten as the prompt is assembled, by a new fork-only
packages/codev-brand, at the three points every model-visible string flows through:SystemPrompt.provider— upstream's branch chain is renamedselect()and leftbyte-identical;
provider()returnsselect(model).map(rebrand)description— coverstool/lsp.txtand anything added latercustomize-opencode→customize-codev, descriptionand body rebranded; the old name still resolves, so existing configs and
skillpermissionrules keep working
Why a transform instead of editing the prompt files. They are upstream's highest-churn
text. Editing them buys 10 permanent conflict sites, and when upstream adds a new brand
mention in a new paragraph it lands silently unrebranded. Leaving them byte-identical means
their sync hunks always apply, and new mentions get rebranded on arrival.
initialize.txtis edited directly instead — it is already fork-divergent and barely churns.
Identity is also stated in the environment block, because an agent that defines its own
promptreplaces the provider prompt wholesale (llm/request.ts) and would otherwise haveno idea what product it is.
A blind find-replace would break things, so the rules split on a preserve-pattern first.
customize-opencode.mdalone contains@opencode-ai/plugin, the plugin namesopencode-gemini-auth/opencode-foo@1.2.3/opencode-bar, andhttps://opencode.ai/config.json×5 — all identifiers the fork deliberately keeps, sorewriting them would make the model's advice wrong.
opencode://,ai.opencode.*andopencode.dbare preserved for the same reason.2. Half the docs links were 404ing
Chasing the docs URL the prompt should carry turned up a live bug in the links the app
opens in the user's browser. Their base was missing the site's locale segment and its
/codesection. Probed against the live site (paths shown, host omitted — see below):/codev/docs/codev/en/docs(worked by luck)/codev/docs/themes/codev/docs/providers/codev/docs/code/githubSo settings theme "Learn more" (both settings screens), the custom-provider dialog link,
and the CLI's Cloudflare-gateway hint were all sending users to a 404. All 10 docs links —
those three plus TUI "Open docs", the desktop Documentation menu item, the sidebar Help
buttons, and the GitHub-agent hint — now use the new base, which resolves 200 for every
one of them. Trailing slashes are dropped (the site 308-redirects
…/themes/to…/themes) and the#custom-provider/#cloudflare-ai-gatewayanchor ids were confirmedpresent on the new providers page.
Not moved: the ripgrep download mirror. It is a
public/static asset, notlocale-routed content — old path 200,
/en/docs/code/downloads/…404. Moving it wouldhave bricked the offline-rg fallback on GitHub-blocked networks. Called out as an explicit
exception in its DIVERGENCES row.
3. One encoded host instead of ten plaintext ones
The host was spelled out in plaintext at all 10 call sites, which defeats the point of the
base64 host already used in
codev-gateway/src/const.tsandcore/src/ripgrep/binary.ts("keep the host out of casual greps, not as a secret"). Rather than repeat an
atob(...)per site, every link now derives from a single
DOCS_URLin@codev/brand, which is itselfbase64-encoded — one encoded host in the tree, and one place to fix when the site moves
again.
appandtuipick up a@codev/brandworkspace dependency; it is a zero-dep leafusing only
atob, so it is safe in client runtime code.The two tests that pinned a URL literal keep pinning it, base64-encoded, so they still
verify the value independently of the constant under test. CHANGELOG and DIVERGENCES prose
now points at
DOCS_URL/MIRRORinstead of spelling the host out — same effect asencoding, without turning the fork's conflict-resolution guidance into base64.
A grep for the host across the repo now returns nothing.
DIVERGENCES.mdgets 5 new rows for the prompt work, plus rows forpackages/app/package.jsonand
.prettierignoreand an extendedpackages/tui/package.jsonrow; the docs-links row andURLs bullet are rewritten around
DOCS_URLand gaingithub.handler.ts, which was previouslyuntracked.
DIVERGENCES.mditself is now prettier-ignored: formatting it realigns all ~120table rows, turning any one-row edit into a whole-file diff in the one place that records
upstream-conflict guidance. Nothing formatted it before, so that pins the status quo. Its "Known rebrand gaps"
section no longer lists prompt files as deliberately upstream and documents the transform and
its preserve-list instead.
How did you verify your code works?
packages/opencode/test/session/brand.test.tsis the tripwire: it scans everyembedded
.txtundersrc/plus the skill body and fails when a brand mention isn'tcovered by the rules. That is the part per-file edits can't give you — the next upstream
sync that introduces new wording fails CI instead of shipping. It also asserts each
provider()branch names the fork and routes support at the fork's URLs.packages/codev-brand/test/brand.test.ts— 7 tests: rewrites, preserved identifiers,config-file renames, CLI-invocation handling, idempotence.
codev debug skill): 19 rebranded mentions in the liveskill body, with
https://opencode.ai/config.json×5,opencode-gemini-auth,opencode-foo,opencode-barand@opencode-ai/pluginall intact.curl -sL -o /dev/null -w '%{http_code}'— the table above is that output. Anchor idschecked by grepping the rendered providers page.
bun typecheckclean — all 32 workspace tasks, which is what the pre-push hook runs. Test runs:test/session+test/agent(429),test/tool(383),test/skill+test/cli/help(26),core
test/plugin(226).session/compaction.test.ts > stops quickly when aborted during retry backofffails identically on a cleanmain(verified by stashing).Screenshots / recordings
Not a UI change. Before, on
MiniMax/MiniMax-M3:Checklist
🤖 Generated with Claude Code