Rename desktop app from "Cline Code" to "Cline" (#13401)

* Rename desktop app from Cline Code to Cline

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

* Format touched Rust test assertions

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
This commit is contained in:
Saoud Rizwan
2026-08-19 15:29:00 -07:00
committed by GitHub
parent 36397f47eb
commit 878faf0a95
25 changed files with 54 additions and 61 deletions
+4 -4
View File
@@ -1,11 +1,11 @@
---
name: publish-desktop
description: Use when preparing, tagging, and publishing a Cline Code desktop app (apps/examples/desktop-app) release — stable (desktop-vX.Y.Z from main) or beta (desktop-vX.Y.Z-beta.N from desktop-experimental, shipped as the side-by-side "Cline Code Beta" app). Guides changelog drafting, version bumps in package.json + tauri.conf.json, tagging, and the desktop-publish GitHub workflow that builds, signs, notarizes, and updates the per-channel auto-update feed.
description: Use when preparing, tagging, and publishing a Cline desktop app (apps/examples/desktop-app) release — stable (desktop-vX.Y.Z from main) or beta (desktop-vX.Y.Z-beta.N from desktop-experimental, shipped as the side-by-side "Cline Beta" app). Guides changelog drafting, version bumps in package.json + tauri.conf.json, tagging, and the desktop-publish GitHub workflow that builds, signs, notarizes, and updates the per-channel auto-update feed.
---
# Desktop App Release
Use this skill when the user asks to release the desktop app, publish Cline Code, cut a desktop beta, bump the desktop version, create a `desktop-vX.Y.Z` (or `desktop-vX.Y.Z-beta.N`) tag, or trigger the desktop publish workflow.
Use this skill when the user asks to release the desktop app, publish the Cline desktop app, cut a desktop beta, bump the desktop version, create a `desktop-vX.Y.Z` (or `desktop-vX.Y.Z-beta.N`) tag, or trigger the desktop publish workflow.
> Working directory: run every command below from the repository root.
@@ -14,8 +14,8 @@ Desktop releases are macOS-only today (a single signed + notarized universal DMG
## Release contract
- Two channels, one workflow (`channel` input on `desktop-publish.yml`):
- **stable** — tag `desktop-vX.Y.Z` (no suffix; the workflow rejects prerelease suffixes on this channel), cut from `main`, feeds the rolling `desktop-latest` release, ships as "Cline Code".
- **beta** — tag `desktop-vX.Y.Z-beta.N`, cut from `desktop-experimental`, feeds the rolling `desktop-beta` release, ships as "Cline Code Beta" (separate bundle identifier `bot.cline.app.beta`; installs side by side with stable). Built with the extra `src-tauri/tauri.beta.conf.json` overlay. Process background: `apps/examples/desktop-app/EXPERIMENTAL.md`.
- **stable** — tag `desktop-vX.Y.Z` (no suffix; the workflow rejects prerelease suffixes on this channel), cut from `main`, feeds the rolling `desktop-latest` release, ships as "Cline".
- **beta** — tag `desktop-vX.Y.Z-beta.N`, cut from `desktop-experimental`, feeds the rolling `desktop-beta` release, ships as "Cline Beta" (separate bundle identifier `bot.cline.app.beta`; installs side by side with stable). Built with the extra `src-tauri/tauri.beta.conf.json` overlay. Process background: `apps/examples/desktop-app/EXPERIMENTAL.md`.
- Version sources (must match each other and the tag): `apps/examples/desktop-app/package.json` and `apps/examples/desktop-app/src-tauri/tauri.conf.json`. (`src-tauri/Cargo.toml` has its own version but `tauri.conf.json` overrides it; no need to touch it.)
- Beta versions are prereleases of the **next** stable: stable `0.0.13` → betas `0.0.14-beta.1`, `-beta.2`, … Once a stable ≥ the beta base ships, the next beta bumps its base (`0.0.15-beta.1`).
- Release prep includes approved release notes, the version bumps, and an `apps/examples/desktop-app/CHANGELOG.md` update — committed on `main` for stable, on `desktop-experimental` for beta.
+8 -8
View File
@@ -111,7 +111,7 @@ jobs:
fi
ANCESTOR_REF=main
FEED=desktop-latest
PRODUCT="Cline Code"
PRODUCT="Cline"
;;
beta)
if ! printf "%s\n" "$TAG" | grep -Eq '^desktop-v[0-9]+\.[0-9]+\.[0-9]+-beta\.[0-9]+$'; then
@@ -120,7 +120,7 @@ jobs:
fi
ANCESTOR_REF=desktop-experimental
FEED=desktop-beta
PRODUCT="Cline Code Beta"
PRODUCT="Cline Beta"
;;
*)
echo "unknown channel: ${CHANNEL}"
@@ -435,7 +435,7 @@ jobs:
OUT="dist/publish"
mkdir -p "$OUT"
# "Cline Code" -> Cline-Code, "Cline Code Beta" -> Cline-Code-Beta
# "Cline" -> Cline, "Cline Beta" -> Cline-Beta
PREFIX="${PRODUCT// /-}"
DMG=$(find "$BUNDLE_DIR/dmg" -name '*.dmg' -print -quit)
@@ -607,14 +607,14 @@ jobs:
if ! gh release view "$FEED" >/dev/null 2>&1; then
if [ "$CHANNEL" = "beta" ]; then
gh release create "$FEED" \
--title "Cline Code desktop beta (auto-update feed)" \
--title "Cline desktop beta (auto-update feed)" \
--notes "Rolling release backing the beta desktop app auto-updater. The latest.json asset points at the newest desktop-vX.Y.Z-beta.N release. Only beta installs poll this feed; stable installs use desktop-latest. Do not delete." \
--latest=false \
--prerelease \
--target "$(git rev-parse HEAD)"
else
gh release create "$FEED" \
--title "Cline Code desktop (auto-update feed)" \
--title "Cline desktop (auto-update feed)" \
--notes "Rolling release backing the desktop app auto-updater. The latest.json asset points at the newest desktop-vX.Y.Z release. Do not delete." \
--latest=false \
--target "$(git rev-parse HEAD)"
@@ -628,7 +628,7 @@ jobs:
TAG: ${{ needs.validate.outputs.tag }}
FEED: ${{ needs.validate.outputs.feed }}
run: |
echo "Published Cline Code desktop v${VERSION}"
echo "Published Cline desktop v${VERSION}"
echo "Release: https://github.com/${GITHUB_REPOSITORY}/releases/tag/${TAG}"
echo "Auto-update feed refreshed: https://github.com/${GITHUB_REPOSITORY}/releases/download/${FEED}/latest.json"
@@ -639,12 +639,12 @@ jobs:
token: ${{ secrets.SLACK_RELEASE_BOT_TOKEN }}
payload: |
channel: "C0APVKGGZFC"
text: "Cline Code desktop v${{ needs.validate.outputs.version }}${{ needs.validate.outputs.channel == 'beta' && ' (beta)' || '' }}"
text: "Cline desktop v${{ needs.validate.outputs.version }}${{ needs.validate.outputs.channel == 'beta' && ' (beta)' || '' }}"
blocks:
- type: "section"
text:
type: "mrkdwn"
text: "Cline Code desktop v${{ needs.validate.outputs.version }}${{ needs.validate.outputs.channel == 'beta' && ' (beta)' || '' }}"
text: "Cline desktop v${{ needs.validate.outputs.version }}${{ needs.validate.outputs.channel == 'beta' && ' (beta)' || '' }}"
- type: "section"
text:
type: "mrkdwn"
+1 -1
View File
@@ -88,7 +88,7 @@ function summarizeClient(client: TrackedClient): {
normalizedType === "code-sidecar-observer" ||
normalizedType === "code-sidecar-list"
) {
return { key: "code-app", label: "Code App", name: "Code App" };
return { key: "code-app", label: "Cline Desktop", name: "Cline Desktop" };
}
return {
key: client.clientId,
+1 -1
View File
@@ -1,4 +1,4 @@
# Cline Code Desktop Changelog
# Cline Desktop Changelog
## 0.0.14
+3 -3
View File
@@ -1,7 +1,7 @@
# Desktop Experimental Branch & Beta Channel
How experimental desktop features are developed on the `desktop-experimental`
branch, shipped to users as **Cline Code Beta**, and graduated into `main`.
branch, shipped to users as **Cline Beta**, and graduated into `main`.
The release mechanics (workflow internals, secrets) live in
[`.github/workflows/desktop-publish.yml`](../../../.github/workflows/desktop-publish.yml)
and the `publish-desktop` skill
@@ -12,8 +12,8 @@ this doc is the process.
The beta is a **separate app**, not a mode of the stable app:
- Product name `Cline Code Beta`, bundle identifier `bot.cline.app.beta`
(stable is `Cline Code` / `bot.cline.app`) — set by
- Product name `Cline Beta`, bundle identifier `bot.cline.app.beta`
(stable is `Cline` / `bot.cline.app`) — set by
[`src-tauri/tauri.beta.conf.json`](./src-tauri/tauri.beta.conf.json), which
is layered over `tauri.release.conf.json` at build time.
- Both apps install and run **side by side**, so people can compare beta
+1 -1
View File
@@ -55,7 +55,7 @@ lost: the `desktop-latest` release/tag (its feed URL is baked into shipped
apps) and the updater private key (`TAURI_SIGNING_PRIVATE_KEY` — without it,
shipped apps can't verify new updates).
There is also a beta channel ("Cline Code Beta", a separate app that installs
There is also a beta channel ("Cline Beta", a separate app that installs
side by side with stable) cut from the `desktop-experimental` branch and
served by the rolling `desktop-beta` release — the same never-delete rule
applies to it. The experimental-branch process and beta release flow live in
@@ -121,7 +121,7 @@ const main = () => {
const notes = notesFile
? readFileSync(notesFile, "utf8").trim()
: `Cline Code v${version}`;
: `Cline v${version}`;
const manifest = buildUpdateManifest({
version,
@@ -15,7 +15,7 @@ const BOOLEAN_FLAGS = new Set(["--allow-unsigned-mac", "--skip-build"]);
const VALUE_FLAGS = new Set(["--platform", "--target"]);
const VALID_FLAGS = [...BOOLEAN_FLAGS, ...VALUE_FLAGS];
const APP_NAME = "Cline Code";
const APP_NAME = "Cline";
const APP_ROOT = path.resolve(import.meta.dir, "..");
const BUNDLE_ROOT = path.join(
APP_ROOT,
@@ -49,7 +49,7 @@ const sessionManager = await ClineCore.create({
workspaceRoot,
cwd: workspaceRoot,
clientType: "code-sidecar",
displayName: "Code App sidecar",
displayName: "Cline Desktop sidecar",
},
capabilities: {
requestToolApproval: async (request) => {
@@ -80,7 +80,7 @@ describe("Code sidecar runtime capabilities", () => {
});
});
it("registers Code App capability factory with core", async () => {
it("registers the desktop capability factory with core", async () => {
const { createSidecarContext, initializeSessionManager } = await import(
"./context"
);
@@ -102,7 +102,7 @@ describe("Code sidecar runtime capabilities", () => {
workspaceRoot: "/workspace/project",
cwd: "/workspace/project",
clientType: "code-sidecar",
displayName: "Code App sidecar",
displayName: "Cline Desktop sidecar",
}),
}),
);
@@ -113,7 +113,7 @@ describe("Code sidecar runtime capabilities", () => {
expect.objectContaining({
url: "ws://127.0.0.1:25463/hub",
clientType: "code-sidecar-observer",
displayName: "Code App observer",
displayName: "Cline Desktop observer",
}),
);
});
@@ -578,7 +578,7 @@ describe("Code sidecar runtime capabilities", () => {
hub: expect.objectContaining({
strategy: "require-hub",
clientType: "code-sidecar",
displayName: "Code App sidecar",
displayName: "Cline Desktop sidecar",
}),
}),
);
+2 -2
View File
@@ -851,7 +851,7 @@ export async function initializeSessionManager(
workspaceRoot: ctx.workspaceRoot,
cwd: ctx.workspaceRoot,
clientType: "code-sidecar",
displayName: "Code App sidecar",
displayName: "Cline Desktop sidecar",
},
});
@@ -899,7 +899,7 @@ export async function ensureSharedHubClient(
const client = new NodeHubClient({
url,
clientType: "code-sidecar-observer",
displayName: "Code App observer",
displayName: "Cline Desktop observer",
workspaceRoot: ctx.workspaceRoot,
cwd: ctx.workspaceRoot,
});
@@ -58,7 +58,7 @@ describe("desktop observability", () => {
expect(mocks.createClineTelemetryServiceConfig).toHaveBeenCalledWith({
metadata: expect.objectContaining({
cline_type: "desktop",
platform: "Cline Code",
platform: "Cline",
}),
});
expect(mocks.createConfiguredTelemetryHandle).toHaveBeenCalledWith(
@@ -30,7 +30,7 @@ export function createDesktopObservability(): DesktopObservability {
metadata: {
extension_version: version,
cline_type: "desktop",
platform: "Cline Code",
platform: "Cline",
platform_version: process.version,
os_type: os.platform(),
os_version: os.version(),
@@ -3,6 +3,6 @@
<plist version="1.0">
<dict>
<key>NSMicrophoneUsageDescription</key>
<string>Cline Code uses the microphone to transcribe speech into chat input.</string>
<string>Cline uses the microphone to transcribe speech into chat input.</string>
</dict>
</plist>
@@ -6,7 +6,7 @@ use std::sync::OnceLock;
use tauri::AppHandle;
const DEV_APP_DIRECTORY: &str = "notification-identity";
const DEV_BUNDLE_NAME: &str = "Cline Code.app";
const DEV_BUNDLE_NAME: &str = "Cline.app";
const LAUNCH_SERVICES_REGISTER: &str = "/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister";
static CONFIGURATION: OnceLock<Result<(), String>> = OnceLock::new();
@@ -200,17 +200,13 @@ mod tests {
fs::write(&executable, b"test executable").unwrap();
fs::write(&icon, b"test icon").unwrap();
let bundle = create_dev_application_bundle(
&executable,
&icon,
"bot.cline.app.dev",
"Cline Code Dev",
)
.unwrap();
let bundle =
create_dev_application_bundle(&executable, &icon, "bot.cline.app.dev", "Cline Dev")
.unwrap();
let plist = fs::read_to_string(bundle.join("Contents/Info.plist")).unwrap();
assert!(plist.contains("<string>bot.cline.app.dev</string>"));
assert!(plist.contains("<string>Cline Code Dev</string>"));
assert!(plist.contains("<string>Cline Dev</string>"));
assert_eq!(
fs::read_link(bundle.join("Contents/MacOS/cline-app")).unwrap(),
executable
@@ -162,7 +162,7 @@ fn running_sessions_text(running_sessions: u32) -> String {
}
// app_name is package_info().name (the configured productName), so beta
// builds ("Cline Code Beta") identify themselves in the tooltip too.
// builds ("Cline Beta") identify themselves in the tooltip too.
fn tray_tooltip_text(app_name: &str, running_sessions: u32) -> String {
if running_sessions == 0 {
app_name.to_string()
@@ -1036,7 +1036,7 @@ fn setup_tray_icon(app: &tauri::App) -> tauri::Result<()> {
.text(
TRAY_OPEN_MENU_ID,
// package_info().name is the configured productName, so beta
// builds ("Cline Code Beta") identify themselves in the tray too.
// builds ("Cline Beta") identify themselves in the tray too.
format!(
"{} v{}",
app.package_info().name,
@@ -1326,14 +1326,11 @@ mod tests {
assert_eq!(running_sessions_text(0), "0 sessions running");
assert_eq!(running_sessions_text(1), "1 session running");
assert_eq!(running_sessions_text(3), "3 sessions running");
assert_eq!(tray_tooltip_text("Cline Code", 0), "Cline Code");
assert_eq!(tray_tooltip_text("Cline", 0), "Cline");
assert_eq!(tray_tooltip_text("Cline", 3), "Cline — 3 sessions running");
assert_eq!(
tray_tooltip_text("Cline Code", 3),
"Cline Code3 sessions running"
);
assert_eq!(
tray_tooltip_text("Cline Code Beta", 2),
"Cline Code Beta — 2 sessions running"
tray_tooltip_text("Cline Beta", 2),
"Cline Beta2 sessions running"
);
assert_eq!(tray_badge_text(0), None);
assert_eq!(tray_badge_text(3), Some("3".to_string()));
@@ -1,6 +1,6 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Cline Code Beta",
"productName": "Cline Beta",
"identifier": "bot.cline.app.beta",
"plugins": {
"updater": {
@@ -1,6 +1,6 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Cline Code",
"productName": "Cline",
"version": "0.0.14",
"identifier": "bot.cline.app",
"build": {
@@ -21,7 +21,7 @@
"windows": [
{
"label": "main",
"title": "Cline Code",
"title": "Cline",
"width": 1500,
"height": 980,
"resizable": true,
@@ -1,5 +1,5 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Cline Code Dev",
"productName": "Cline Dev",
"identifier": "bot.cline.app.dev"
}
@@ -536,7 +536,7 @@ describe("AgentSidebar session organization", () => {
const titleBar = container.querySelector("[data-tauri-drag-region]");
expect(titleBar).not.toBeNull();
expect(titleBar?.textContent).not.toContain("Cline Code");
expect(titleBar?.textContent).not.toContain("Cline");
await click(
container.querySelector('[aria-label="Previous page"]') as Element,
@@ -104,8 +104,8 @@ export function HubUpdateRequiredDialog() {
{mismatch?.hubCoreVersion
? ` (core ${mismatch.hubCoreVersion})`
: ""}
, and it no longer matches this app. Update and restart Cline Code
to stay in sync with the running Hub.
, and it no longer matches this app. Update and restart Cline to
stay in sync with the running Hub.
</AlertDialogDescription>
{updateHint ? (
<AlertDialogDescription>{updateHint}</AlertDialogDescription>
@@ -9,8 +9,8 @@
* See apps/examples/desktop-app/EXPERIMENTAL.md for the channel model.
*/
export const STABLE_PRODUCT_NAME = "Cline Code";
export const BETA_PRODUCT_NAME = "Cline Code Beta";
export const STABLE_PRODUCT_NAME = "Cline";
export const BETA_PRODUCT_NAME = "Cline Beta";
export function isBetaVersion(version: string | null | undefined): boolean {
return typeof version === "string" && version.includes("-beta");
@@ -49,7 +49,7 @@ describe("desktop window title", () => {
it("titles beta builds with the beta product name", async () => {
const { buildDesktopWindowTitle } = await importFresh();
expect(buildDesktopWindowTitle("0.0.14-beta.1")).toBe(
"Cline Code Beta v0.0.14-beta.1",
"Cline Beta v0.0.14-beta.1",
);
});
+2 -2
View File
@@ -268,8 +268,8 @@ function summarizeClient(client: TrackedClient): {
) {
return {
key: "code-app",
label: "Code App",
name: "Code App",
label: "Cline Desktop",
name: "Cline Desktop",
};
}
return {
+1 -1
View File
@@ -30,7 +30,7 @@ flowchart TD
shared["@cline/shared"] --> llms["@cline/llms"] & agents["@cline/agents"] & core["@cline/core"]
llms --> agents & core
agents --> core
core --> apps["CLI / VS Code / Code App"]
core --> apps["CLI / VS Code / Desktop App"]
```
Rules: