Skip to content

fix(prompt): rebrand every model-visible surface to CoDev Code - #165

Merged
quickbeard merged 5 commits into
mainfrom
rebrand-agent-identity
Aug 7, 2026
Merged

quickbeard merged 5 commits into
mainfrom
rebrand-agent-identity

Conversation

@quickbeard

@quickbeard quickbeard commented Aug 6, 2026 •

Copy link
Copy Markdown
Owner

Issue for this PR

Closes #

Type of change

  • Bug fix

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/opencode and read the docs at opencode.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 are
upstream's verbatim — 20 brand mentions across identity lines, the /help line, the
feedback 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 renamed select() and left
    byte-identical; provider() returns select(model).map(rebrand)
  • the tool registry's assembled description — covers tool/lsp.txt and anything added later
  • the built-in config skill — renamed customize-opencode → customize-codev, description
    and body rebranded; the old name still resolves, so existing configs and skill permission
    rules 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.txt
is 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
prompt replaces the provider prompt wholesale (llm/request.ts) and would otherwise have
no idea what product it is.

A blind find-replace would break things, so the rules split on a preserve-pattern first.
customize-opencode.md alone contains @opencode-ai/plugin, the plugin names
opencode-gemini-auth / opencode-foo@1.2.3 / opencode-bar, and
https://opencode.ai/config.json ×5 — all identifiers the fork deliberately keeps, so
rewriting them would make the model's advice wrong. opencode://, ai.opencode.* and
opencode.db are 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
/code section. Probed against the live site (paths shown, host omitted — see below):

old URL result
/codev/docs 307 → /codev/en/docs (worked by luck)
/codev/docs/themes 404
/codev/docs/providers 404
/codev/docs/code/github 200

So 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-gateway anchor ids were confirmed
present on the new providers page.

Not moved: the ripgrep download mirror. It is a public/ static asset, not
locale-routed content — old path 200, /en/docs/code/downloads/… 404. Moving it would
have 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.ts and core/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_URL in @codev/brand, which is itself
base64-encoded — one encoded host in the tree, and one place to fix when the site moves
again. app and tui pick up 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 under test. CHANGELOG and DIVERGENCES prose
now points at DOCS_URL / MIRROR instead of spelling the host out — same effect as
encoding, without turning the fork's conflict-resolution guidance into base64.

A grep for the host across the repo now returns nothing.

DIVERGENCES.md gets 5 new rows for the prompt work, plus rows for packages/app/package.json
and .prettierignore and an extended packages/tui/package.json row; the docs-links row and
URLs bullet are rewritten around DOCS_URL and gain github.handler.ts, which was previously
untracked. DIVERGENCES.md itself is now prettier-ignored: formatting it realigns all ~120
table 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?

  • New packages/opencode/test/session/brand.test.ts is the tripwire: it scans every
    embedded .txt under src/ plus the skill body and fails when a brand mention isn't
    covered 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.
  • New packages/codev-brand/test/brand.test.ts — 7 tests: rewrites, preserved identifiers,
    config-file renames, CLI-invocation handling, idempotence.
  • End-to-end through the real CLI (codev debug skill): 19 rebranded mentions in the live
    skill body, with https://opencode.ai/config.json ×5, opencode-gemini-auth,
    opencode-foo, opencode-bar and @opencode-ai/plugin all intact.
  • Every docs URL, old and new, probed against the live site with
    curl -sL -o /dev/null -w '%{http_code}' — the table above is that output. Anchor ids
    checked by grepping the rendered providers page.
  • Root bun typecheck clean — 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).
  • One pre-existing unrelated failure: session/compaction.test.ts > stops quickly when aborted during retry backoff fails identically on a clean main (verified by stashing).

Screenshots / recordings

Not a UI change. Before, on MiniMax/MiniMax-M3:

who are you
I'm opencode, an interactive CLI tool that helps with software engineering tasks.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

🤖 Generated with Claude Code

quickbeard and others added 5 commits August 6, 2026 19:32
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>
@quickbeard
quickbeard merged commit 9aa900c into main Aug 7, 2026
0 of 5 checks passed
@quickbeard
quickbeard deleted the rebrand-agent-identity branch August 7, 2026 09:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant