Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 5 additions & 20 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ name: release

# Build, sign, and publish a Redrob Code release.
#
# Versions are ours and start at 0.1.0. They are NOT derived from the upstream version
# this tree is built on: that provenance lives in `UPSTREAM_VERSION` at the repository
# root, which the release notes read. See docs/VERSIONING.md for why the old
# `1.18.31-redrob.N` shape was retired.
# Versions start at 0.1.0 and follow semver. See docs/VERSIONING.md.
#
# `workflow_dispatch` ONLY. This job reaches real signing credentials, so it must never be
# reachable from a pull request, a comment, or a schedule. Dispatch requires write access
Expand Down Expand Up @@ -56,7 +53,6 @@ jobs:
outputs:
version: ${{ steps.compute.outputs.version }}
tag: ${{ steps.compute.outputs.tag }}
upstream: ${{ steps.compute.outputs.upstream }}
# The one commit this release is. Every later job checks THIS out, not `develop`:
# a merge that lands while the build runs must not make the tag point at code the
# binaries were not built from (it did, once, with a merge two minutes after dispatch).
Expand All @@ -76,7 +72,7 @@ jobs:
run: |
set -euo pipefail
# Only plain `vMAJOR.MINOR.PATCH` counts. Anything carrying a `-` is a prerelease
# or a legacy upstream-derived tag, and letting one of those win here would make
# or a legacy `-redrob.N` tag, and letting one of those win here would make
# the next version a continuation of a line we deliberately left behind.
latest="$(git tag --list 'v[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname \
| grep -v -- '-' | head -n1 || true)"
Expand All @@ -103,19 +99,10 @@ jobs:
echo "::error::v$version already exists; a release cannot be recomputed onto an existing tag"
exit 1
fi
# The upstream version the tag no longer carries. Absent is a defect rather than
# a blank field: the release notes state what this is built on, and a release
# that cannot say so should not be published.
if [ ! -s UPSTREAM_VERSION ]; then
echo "::error::UPSTREAM_VERSION is missing or empty; see docs/VERSIONING.md"
exit 1
fi
upstream="$(tr -d '[:space:]' < UPSTREAM_VERSION)"
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "tag=v$version" >> "$GITHUB_OUTPUT"
echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
echo "upstream=$upstream" >> "$GITHUB_OUTPUT"
echo "::notice::releasing v$version (built on opencode v$upstream)"
echo "::notice::releasing v$version"

build:
needs: version
Expand Down Expand Up @@ -548,18 +535,16 @@ jobs:
- name: Create and push the tag
env:
TAG: ${{ needs.version.outputs.tag }}
UPSTREAM: ${{ needs.version.outputs.upstream }}
run: |
set -euo pipefail
# An annotated tag records a tagger, and a bare runner has no git identity, so
# `git tag -a` fails with "empty ident name" AFTER the build, the signing and the
# notarization have all succeeded -- the most expensive possible place to
# discover it. The tag stays annotated rather than lightweight because it is what
# a release points at and it should say who made it and when, and its message
# carries the upstream version the release notes no longer state.
# a release points at and it should say who made it and when.
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git tag -a "$TAG" -m "Redrob Code $TAG (built on opencode v$UPSTREAM)" 2>/dev/null || \
git tag -a "$TAG" -m "Redrob Code $TAG" 2>/dev/null || \
echo "tag $TAG already exists locally"
# A release that fails after tagging must be retryable. Without this a single
# transient failure in the publish job leaves the version permanently unusable:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ jobs:
# Docs.
*.md|docs/*|LICENSE*|CHANGELOG*) continue ;;
# Whole-file version markers.
VERSION|REDROB_VERSION|UPSTREAM_VERSION|*-version.txt|*/VERSION) continue ;;
VERSION|REDROB_VERSION|*-version.txt|*/VERSION) continue ;;
# Manifests and lockfiles: allowed only if every changed line is a version string.
package.json|*/package.json|bun.lock|Cargo.toml|*/Cargo.toml|Cargo.lock|*/Cargo.lock)
if git diff -U0 HEAD^1 HEAD -- "$f" \
Expand Down
7 changes: 3 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# AGENTS.md

Redrob Code: the engine behind Redrob Cowork, a fork of OpenCode. It is consumed as a pinned
Redrob Code: the engine behind Redrob Cowork. It is consumed as a pinned
version rather than a branch — `constants.json` in `redrob-cowork` names the release the desktop app
ships, so **a change here reaches users only through a release and a pin bump**, never by merging.

Expand All @@ -16,7 +16,7 @@ Gitflow. **`develop` is the default branch and the base of every pull request.**

Branch names are `<type>/<slug>` with the type from the redrob-labs organization list:
`feat fix chore docs test refactor perf ci build sync hotfix release` (`docs/BRANCHING.md` has the
ones this repository actually uses: `feat/<slug>`, `sync/upstream-<tag>`, `hotfix/<slug>`,
ones this repository actually uses: `feat/<slug>`, `hotfix/<slug>`,
`release/<version>`). Do not invent a prefix, and in particular do not name a branch after the tool
or agent that made it: a branch name says what the change is, not who typed it.

Expand Down Expand Up @@ -62,8 +62,7 @@ Full detail in `docs/BRANCHING.md`.
instead.
- Judge by the exit code, not by a pass count. A suite can print `0 fail` and still exit non-zero
when a file fails to load: its tests are never counted and a `(fail)` grep finds nothing.
- The version line is ours and starts at `0.1.0`. It does not encode the upstream version, which
lives in `UPSTREAM_VERSION` at the repository root and is what the release notes read. Do not
- The version line starts at `0.1.0`. Do not
reintroduce a `-redrob.N` suffix: it made every release a semver prerelease, which sorts below
the release it precedes and made `getReleaseType()` call everything a patch. See
`docs/VERSIONING.md`.
Expand Down
21 changes: 9 additions & 12 deletions README.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,12 @@

---

레드롭 코드는 [opencode](https://github.com/anomalyco/opencode)의 포크이며 MIT 라이선스로
배포됩니다. opencode 프로젝트 및 그 관리자와 제휴, 보증, 지원 관계가 없습니다. 이름은
이 소프트웨어의 출처를 밝히기 위해서만 사용합니다.

포크가 바꾼 것은 세 가지입니다. 공급자 모델을 벤더별 키가 아닌 하나의 레드롭 워크스페이스
엔드포인트로 바꿨고, 한국어를 일급 로케일로 다루며, 호스팅 콘솔이 아닌 터미널 클라이언트에
맞게 패키지를 줄였습니다. 릴리스는 `0.1.0` 부터 시작하는 자체 버전 체계를 사용합니다.
빌드가 기반한 상류 버전은 `UPSTREAM_VERSION` 에 기록되고 릴리스 노트에 표시됩니다.
레드롭 코드는 MIT 라이선스로 배포됩니다. 서드파티 라이선스 고지는
[NOTICE.md](./NOTICE.md)에 있습니다.

레드롭 코드는 벤더별 키가 아닌 하나의 레드롭 워크스페이스 엔드포인트를 쓰고,
한국어를 일급 로케일로 다루며, 호스팅 콘솔이 아닌 터미널 클라이언트에 맞춘
패키지로 구성됩니다. 릴리스는 `0.1.0` 부터 시작하는 자체 버전 체계를 사용합니다.
자세한 내용은 `docs/VERSIONING.md` 를 참고하세요.

### 설치
Expand Down Expand Up @@ -126,7 +124,7 @@ id를 쓰는 설정·에이전트·명령은 `redrob/auto`로 바꾸세요.
프로젝트 설정이 전역 설정 위에 병합됩니다. 에이전트에게 설정 파일을 수정하라고 요청하면
실제 스키마가 담긴 내장 스킬을 불러오므로, `redrob`이 자기 설정을 직접 구성할 수 있습니다.

`.opencode/`는 이전 체크아웃 호환을 위해 계속 읽지만, 새 파일은 `.redrob/`에 두세요.
프로젝트 설정 파일은 `.redrob/`에 두세요.

### 범위

Expand All @@ -149,6 +147,5 @@ cd packages/core && bun test # 테스트는 패키지 디렉터리에서 실행

`bun@1.3.14`이 필요하며 pre-push 훅이 이를 검사합니다.

브랜치 모델, 풀 리퀘스트 방법, 그리고 상류 릴리스를 흡수하는 절차는
[docs/BRANCHING.md](./docs/BRANCHING.md)에 있습니다. 마지막 항목이 포크에서 가장
중요하면서 가장 틀리기 쉬운 작업입니다.
브랜치 모델과 풀 리퀘스트 방법은
[docs/BRANCHING.md](./docs/BRANCHING.md)에 있습니다.
25 changes: 10 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,15 @@ Redrob Code, the AI coding agent for your terminal.

---

Redrob Code is the AI coding agent for your terminal, a fork of opencode with Korean as a first-class locale.
Redrob Code is the AI coding agent for your terminal, with Korean as a first-class locale.

Redrob Code is a fork of [opencode](https://github.com/anomalyco/opencode), released
under the MIT License. It is not affiliated with, endorsed by, or supported by the
opencode project or its maintainers: the name is used here only to say where this
software came from.
Redrob Code is released under the MIT License. Third-party license notices are in
[NOTICE.md](./NOTICE.md).

What the fork changes: a different provider model (a single Redrob workspace endpoint
rather than per-vendor keys), Korean as a first-class locale, and a reduced package set
aimed at the terminal client rather than the hosted console. Releases have their own
version line starting at `0.1.0`; the upstream version a build is based on is recorded in
`UPSTREAM_VERSION` and stated in the release notes. See `docs/VERSIONING.md`.
What Redrob Code offers: a single Redrob workspace endpoint
rather than per-vendor keys, Korean as a first-class locale, and a package set
aimed at the terminal client rather than a hosted console. Releases have their own
version line starting at `0.1.0`. See `docs/VERSIONING.md`.

### Installation

Expand Down Expand Up @@ -130,8 +127,7 @@ Two subagents are also available and can be invoked from a message:
Project config is merged over global config. Ask the agent to edit your config and it
loads a built-in skill with the real schema, so `redrob` can also configure itself.

`.opencode/` is still read as a legacy location so older checkouts keep working; write
new files to `.redrob/`.
Project config files live in `.redrob/`.

### Scope

Expand All @@ -155,6 +151,5 @@ cd packages/core && bun test # tests run per package, never from the repo root

`bun@1.3.14` is required; the pre-push hook enforces it.

See [docs/BRANCHING.md](./docs/BRANCHING.md) for the branch model, how to open a pull
request, and how upstream releases are absorbed, the last of which is the operation
that matters most in a fork and the one most easily got wrong.
See [docs/BRANCHING.md](./docs/BRANCHING.md) for the branch model and how to open a pull
request.
1 change: 0 additions & 1 deletion UPSTREAM_VERSION

This file was deleted.

38 changes: 11 additions & 27 deletions docs/BRANCHING.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,24 @@
# Branching

Redrob Code is a **fork** of [opencode](https://github.com/anomalyco/opencode), and that
single fact decides the branching model. A fork's most consequential operation is not a
feature — it is absorbing upstream, which arrives as a large, conflict-prone merge that
can leave the tree broken for a while. Everything below exists so that merge never lands
Redrob Code uses gitflow. Work lands in `develop` first; `main` is released state and
moves only through a reviewed release pull request, so nothing half-integrated lands
somewhere people are cloning from.

## Branches

| Branch | What it is |
|---|---|
| `develop` | **Default.** Integration. Every feature, fix and upstream sync lands here first. |
| `develop` | **Default.** Integration. Every feature and fix lands here first. |
| `main` | The released state. Only ever advanced by a release pull request from `develop`. |
| `feat/<slug>` | One change, opened as a pull request into `develop`. Also `fix/`, `docs/`, `chore/` and the rest of the organization type list, when they describe the change better. |
| `sync/upstream-<tag>` | An upstream absorption, opened as a pull request into `develop`. |
| `hotfix/<slug>` | A fix for something already released, into `main` **and** `develop`. |
| `release/<version>` | Only when a release needs stabilising before it ships. Usually skipped. |

`develop` is the default branch on purpose. Pull requests target it without anyone
having to retarget them, and the people reading the repository's default branch are
contributors. End users install a release artifact; they do not clone.

We do not use `master`, and our branches are deliberately **not** named after upstream's
(`dev`, `main`). When you add an `upstream` remote, `upstream/dev` and `origin/develop`
should not read as the same thing, because they are not.
We do not use `master`.

`release/*` is in the table for completeness and is usually a waste of a branch. Reach
for it only when a release genuinely needs to stabilise while `develop` keeps moving.
Expand All @@ -33,25 +28,15 @@ every pull request: `feat fix chore docs test refactor perf ci build sync hotfix
caller in `.github/workflows/gitflow.yml` is a verbatim copy of the organization template and
carries no list of its own.

## Upstream

Redrob Code no longer merges upstream (opencode) releases; that stopped on 2026-10-26.
Upstream files are ours to change like any other. `UPSTREAM_VERSION` still records the
upstream release this tree was last merged from, because attribution needs it and the
release notes state it.

Some `opencode` strings are still load-bearing and stay as they are. The legacy
`.opencode` config directory is read on purpose so existing checkouts keep working, and
`@gitlab/opencode-gitlab-auth`, `opencode-gitlab-auth` and `opencode-poe-auth` are real
published packages that are not ours to rename. See [NOTICE.md](../NOTICE.md).
Third-party package names such as `@gitlab/opencode-gitlab-auth`, `opencode-gitlab-auth`
and `opencode-poe-auth` are real published packages that are not ours to rename. See
[NOTICE.md](../NOTICE.md) for license attributions.

## Releasing

Versions are ours and start at `0.1.0`. They are not derived from the upstream version the
tree is built on: that is recorded in `UPSTREAM_VERSION` at the repository root, and the
release notes read it so every release still states what it is based on. The old
`-redrob.N` suffix was retired because it made every release a semver prerelease, which
sorts below the release it precedes. See `docs/VERSIONING.md`.
Versions start at `0.1.0` and follow semver. The old `-redrob.N` suffix was retired
because it made every release a semver prerelease, which sorts below the release it
precedes. See `docs/VERSIONING.md`.

Run the `release` workflow from the Actions tab against `develop` with a `bump` of `patch`,
`minor` or `major`. It computes the next version from the newest `v*` tag, builds every
Expand All @@ -74,8 +59,7 @@ shared reusable workflow `redrob-labs/.github/.github/workflows/release-publish.
collects every `release-*` artifact, writes `SHA256SUMS.txt` and `redrob-launcher.json`,
and publishes `v<version>` as latest (as a prerelease when the version carries a `-`). This
workflow passes it the tag and the commit explicitly, because the tag is created by the
`tag` job here rather than by a push. The release notes are GitHub's generated notes; the
upstream version the build is based on is in the tag message and in `UPSTREAM_VERSION`.
`tag` job here rather than by a push. The release notes are GitHub's generated notes.
What stays this repository's: version resolution, the Bun build, macOS notarization, the
Linux archives, the Redrob Launcher asset copies (`launcher-assets.ts`), the `VERSION`
marker `install.sh` reads, and build provenance.
Expand Down
2 changes: 1 addition & 1 deletion docs/LOCAL-HARNESS-BACKENDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ is the same way a local model is already selected, and needs no new request fiel
document recommends one. That is not an inconsistency, it is the surface being
different, and the difference is the whole argument.

A Cowork shim would have to emulate the **OpenCode session API** — sessions, the SSE
A Cowork shim would have to emulate the **Redrob Code session API** — sessions, the SSE
event shape, permissions — which is large, ours, and still changing. A shim that
falls subtly behind produces bugs that look like model bugs. A shim here emulates
**chat-completions**, which is small, published, frozen, and not ours to change. The
Expand Down
6 changes: 3 additions & 3 deletions docs/PROVIDER-AUTH.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ Sources, and the sentences that decide it:
their own applications, or to route requests through Free, Pro, or Max plan
credentials on behalf of their users." And developers "should use API key
authentication through Claude Console or a supported cloud provider." Enforced:
accounts were banned for spoofing the Claude Code harness, and our own upstream
(OpenCode) removed Claude subscription AND Claude API key support on 2026-02-19
accounts were banned for spoofing the Claude Code harness, and other open-source
coding agents removed Claude subscription AND Claude API key support in February 2026
citing Anthropic legal requests ([The Register,
2026-02-20](https://www.theregister.com/software/2026/02/20/anthropic-clarifies-ban-on-third-party-tool-access-to-claude/5014546)).
The only sanctioned subscription route is shipping Claude Code itself,
Expand Down Expand Up @@ -191,7 +191,7 @@ missing.

- Reusing Claude Code's or the Codex CLI's OAuth client id to spend a consumer
subscription from our own harness. Anthropic and Google both ban accounts for
it; our upstream removed the code under legal pressure.
it, and other open-source agents removed such code under legal pressure.
- Collecting, storing or proxying Claude.ai session tokens. Named explicitly in
Anthropic's compliance page.
- Paying for or intermediating another vendor's usage on a user's behalf. Also
Expand Down
26 changes: 6 additions & 20 deletions docs/VERSIONING.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,18 @@
# Versioning

Redrob Code has its own version line starting at `0.1.0`. It is not derived from the
upstream version it is built on.
Redrob Code has its own version line starting at `0.1.0`, following semver.

## Why the version is ours
## Why there is no suffix

Until `0.1.0` the version was the upstream version with a suffix: `1.18.31-redrob.10`
tracked opencode `v1.18.31`, tenth build. That shape encoded useful provenance in the tag
and cost three things to get it.
Before `0.1.0` versions carried a `-redrob.N` suffix, for example `1.18.31-redrob.10`.
That shape cost three things.

1. `1.18.31-redrob.10` is a semver PRERELEASE. Prereleases sort below the release they
precede, so every comparison against a plain version was wrong in a way that looked
right, and `getReleaseType()` classified every single release as a patch because it
only reads major and minor.
2. It made our release numbers look like upstream's. Someone reading `1.18.31` had no
reason to know whose 1.18.31 it was.
3. It tied our release cadence to upstream's. Shipping a fix meant deciding what upstream
version to claim it was against.

## Where the provenance went

`UPSTREAM_VERSION` at the repository root. One line, the upstream version this tree is
merged up to, without the `v`. Upstream is no longer merged (see BRANCHING.md), so it is
fixed at the last release taken, and the release notes read it so every release still
states what it is built on.

That file is the only place the upstream version lives now. Deleting it does not break a
build; it silently removes the answer to "what is this built on", which is worse.
2. The numbers did not say anything about our own release history.
3. Shipping a fix meant deciding what base number to claim it was against.

## Bumping

Expand Down
2 changes: 1 addition & 1 deletion github/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ inputs:
default: ${{ github.token }}

mentions:
description: "Comma-separated list of trigger phrases (case-insensitive). Defaults to '/redrob,/rr,/opencode,/oc' -- the upstream phrases are kept for compatibility"
description: "Comma-separated list of trigger phrases (case-insensitive). Defaults to '/redrob,/rr'"
required: false

variant:
Expand Down
Loading
Loading