feat(#792): add devin-desktop runtime alias for windsurf (#1086)

windsurf now also answers to devin-desktop (CLI --devin-desktop) for the Windsurf→Devin Desktop rebrand; all paths unchanged. The .devin/skills/ workspace migration is split to #1085. Closes #792.
This commit is contained in:
Tom Boucher
2026-06-11 22:32:49 -04:00
committed by GitHub
parent e644819d59
commit 93c5ecd645
9 changed files with 87 additions and 8 deletions

View File

@@ -0,0 +1,6 @@
---
type: Changed
pr: 1086
---
**`devin-desktop` runtime alias for the Windsurf→Devin Desktop rebrand** — the `windsurf` runtime now also answers to `devin-desktop` (CLI `--devin-desktop`), aiding discoverability after Cognition rebranded Windsurf as Devin Desktop. All paths are unchanged — global skills still install to `~/.codeium/windsurf/skills/`. (#1086)

View File

@@ -410,7 +410,7 @@ function selectRuntimesFromArgs(runtimeArgs) {
if (runtimeArgs.includes('--copilot')) selected.push('copilot');
if (runtimeArgs.includes('--antigravity')) selected.push('antigravity');
if (runtimeArgs.includes('--cursor')) selected.push('cursor');
if (runtimeArgs.includes('--windsurf')) selected.push('windsurf');
if (runtimeArgs.includes('--windsurf') || runtimeArgs.includes('--devin-desktop')) selected.push('windsurf');
if (runtimeArgs.includes('--augment')) selected.push('augment');
if (runtimeArgs.includes('--trae')) selected.push('trae');
if (runtimeArgs.includes('--qwen')) selected.push('qwen');

View File

@@ -1209,7 +1209,7 @@ When `runtime` is set, profile tiers (`opus`/`sonnet`/`haiku`) resolve to runtim
| `opencode` | `anthropic/claude-opus-4-8` | `anthropic/claude-sonnet-4-6` | `anthropic/claude-haiku-4-5` | (not used) |
| `copilot` | `claude-opus-4-8` | `claude-sonnet-4-6` | `claude-haiku-4-5` | (not used) |
| `hermes` | `anthropic/claude-opus-4-8` | `anthropic/claude-sonnet-4-6` | `anthropic/claude-haiku-4-5` | (not used) |
| Group B (`kilo`, `cline`, `cursor`, `windsurf`, `augment`, `trae`, `codebuddy`, `antigravity`) | (no built-in default — your runtime handles model selection) | | | |
| Group B (`kilo`, `cline`, `cursor`, `windsurf` (alias: `devin-desktop`), `augment`, `trae`, `codebuddy`, `antigravity`) | (no built-in default — your runtime handles model selection) | | | |
> **How these model IDs are sourced.** The catalog (`bin/shared/model-catalog.json`) pins each runtime's tier defaults to that provider's current frontier IDs, and may intentionally carry forward-dated IDs ahead of a provider's public docs. To verify an ID is live before changing it, check the provider's own source/API — e.g. Gemini: gemini-cli `packages/core/src/config/models.ts` or `gemini --model <id> --prompt ping`; Codex: `codex debug models` or the OpenAI Codex models page; Qwen: Alibaba Model Studio model list. Only change an ID that the provider actually rejects — absence from documentation alone is not proof of invalidity.

View File

@@ -826,7 +826,7 @@ WINDSURF_CONFIG_DIR=~/.codeium/windsurf-next npx @opengsd/gsd-core@latest --wind
| Codex | (per Codex CLI) | `--config-dir` flag |
| Copilot | `~/.copilot` | `COPILOT_CONFIG_DIR` (or `COPILOT_HOME`) |
| Cursor | `~/.cursor` | `CURSOR_CONFIG_DIR` |
| Windsurf | `~/.codeium/windsurf` | `WINDSURF_CONFIG_DIR` |
| Windsurf / Devin Desktop | `~/.codeium/windsurf` | `WINDSURF_CONFIG_DIR` |
| Antigravity | auto-detected | `ANTIGRAVITY_CONFIG_DIR` |
| Augment | `~/.augment` | `AUGMENT_CONFIG_DIR` |
| Trae | `~/.trae` | `TRAE_CONFIG_DIR` |

View File

@@ -308,13 +308,17 @@ CURSOR_CONFIG_DIR=~/.cursor-alt npx @opengsd/gsd-core@latest --cursor --global
---
### Windsurf
### Windsurf / Devin Desktop
Windsurf has rebranded to **Devin Desktop**. Both runtime names are accepted — use either `--windsurf` or `--devin-desktop`.
```bash
npx @opengsd/gsd-core@latest --windsurf --global
# or equivalently:
npx @opengsd/gsd-core@latest --devin-desktop --global
```
Skills land in `~/.codeium/windsurf/`. GSD installs skills, agents, and workspace rules.
Skills land in `~/.codeium/windsurf/` (global path unchanged). GSD installs skills, agents, and workspace rules.
**Override the install directory:**
@@ -456,7 +460,7 @@ A local install writes into the `.claude/` directory at your project root. Local
## Installing prerelease editions (Next / Nightly / Insiders / Preview)
Prerelease editions of runtimes (Windsurf Next, Cursor Nightly, VS Code Insiders, Codex preview channels, etc.) read from a sibling config directory. Set the matching `*_CONFIG_DIR` env var before running the installer:
Prerelease editions of runtimes (Windsurf Next / Devin Desktop Next, Cursor Nightly, VS Code Insiders, Codex preview channels, etc.) read from a sibling config directory. Set the matching `*_CONFIG_DIR` env var before running the installer:
```bash
WINDSURF_CONFIG_DIR=~/.codeium/windsurf-next npx @opengsd/gsd-core@latest --windsurf --global

View File

@@ -43,7 +43,8 @@
"windsurf": [
"windsurf",
"windsurf-cli",
"windsurf-next"
"windsurf-next",
"devin-desktop"
],
"augment": [
"augment",

View File

@@ -23,7 +23,7 @@ const FALLBACK_ALIASES: Readonly<Record<string, string[]>> = {
copilot: ['copilot', 'copilot-cli', 'github-copilot'],
antigravity: ['antigravity', 'antigravity-cli', 'antigravity-agent'],
cursor: ['cursor', 'cursor-cli', 'cursor-nightly'],
windsurf: ['windsurf', 'windsurf-cli', 'windsurf-next'],
windsurf: ['windsurf', 'windsurf-cli', 'windsurf-next', 'devin-desktop'],
augment: ['augment', 'augment-code', 'augment-cli'],
trae: ['trae', 'trae-cli'],
qwen: ['qwen', 'qwen-code', 'qwen-cli'],

View File

@@ -43,6 +43,7 @@ const {
buildRuntimePromptText,
resolveKiloConfigPath,
configureKiloPermissions,
selectRuntimesFromArgs,
} = require('../bin/install.js');
const { getGlobalConfigDir } = require('../gsd-core/bin/lib/runtime-homes.cjs');
@@ -835,6 +836,20 @@ describe('install — changeset CLI lands at scripts/changeset/cli.cjs (#935)',
});
});
// ─── Section 6: Windsurf / devin-desktop alias (#792) ───────────────────────
describe('install — --devin-desktop CLI flag routes to windsurf runtime (#792)', () => {
test('--devin-desktop resolves to ["windsurf"] via selectRuntimesFromArgs', () => {
const runtimes = selectRuntimesFromArgs(['--devin-desktop']);
assert.deepStrictEqual(runtimes, ['windsurf'],
'--devin-desktop must resolve to ["windsurf"] via selectRuntimesFromArgs');
});
test('--windsurf and --devin-desktop both resolve to ["windsurf"]', () => {
assert.deepStrictEqual(selectRuntimesFromArgs(['--windsurf']), ['windsurf']);
assert.deepStrictEqual(selectRuntimesFromArgs(['--devin-desktop']), ['windsurf']);
});
});
// ─── Section N+1: #767 — disallowedTools injection for read-only agents ──────
//
// Verifies (installer-behavioral test — drives install() to a temp dir):

View File

@@ -3,6 +3,7 @@
const { describe, test } = require('node:test');
const assert = require('node:assert/strict');
const path = require('node:path');
const fs = require('node:fs');
const ROOT = path.join(__dirname, '..');
const {
@@ -17,4 +18,56 @@ describe('runtime-name-policy canonical runtime ids', () => {
assert.strictEqual(resolveRuntimeNameFromCandidates('', null, 'kimi'), 'kimi');
assert.strictEqual(canonicalizeRuntimeName('kimi-cli'), null);
});
test('canonicalizes devin-desktop to windsurf (#792)', () => {
assert.strictEqual(canonicalizeRuntimeName('devin-desktop'), 'windsurf');
assert.strictEqual(canonicalizeRuntimeName('DEVIN-DESKTOP'), 'windsurf');
assert.strictEqual(resolveRuntimeNameFromCandidates('devin-desktop'), 'windsurf');
});
});
describe('runtime-name-policy windsurf alias parity — manifest vs FALLBACK_ALIASES (#792)', () => {
// DEFECT.GENERATIVE-FIX: manifest and FALLBACK_ALIASES are manually mirrored;
// this test fails if they diverge for the windsurf key.
const manifestPath = path.join(ROOT, 'gsd-core', 'bin', 'shared', 'runtime-aliases.manifest.json');
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
test('manifest windsurf array includes devin-desktop', () => {
assert.ok(
Array.isArray(manifest.windsurf) && manifest.windsurf.includes('devin-desktop'),
`runtime-aliases.manifest.json windsurf array must include 'devin-desktop'; got: ${JSON.stringify(manifest.windsurf)}`,
);
});
test('FALLBACK_ALIASES windsurf includes devin-desktop (via canonicalization round-trip)', () => {
// The built module merges manifest over FALLBACK_ALIASES; if the manifest is present
// this verifies the combined set. The manifest test above separately guards the manifest.
// Here we verify the live canonicalizer sees devin-desktop -> windsurf.
assert.strictEqual(
canonicalizeRuntimeName('devin-desktop'),
'windsurf',
'devin-desktop must resolve to windsurf via alias lookup',
);
});
test('manifest and FALLBACK_ALIASES windsurf alias sets are identical', () => {
// Read FALLBACK_ALIASES from source to detect manual drift before a build.
const srcPath = path.join(ROOT, 'src', 'runtime-name-policy.cts');
// allow-test-rule: runtime-contract-is-the-product — FALLBACK_ALIASES source text IS the
// product contract for runtimes that can't load the manifest at runtime; verifying
// both surfaces contain the same windsurf aliases catches manual-mirror drift.
const src = fs.readFileSync(srcPath, 'utf8');
const match = src.match(/windsurf:\s*\[([^\]]+)\]/);
assert.ok(match, 'FALLBACK_ALIASES windsurf row must exist in src/runtime-name-policy.cts');
const srcAliases = match[1]
.split(',')
.map(s => s.trim().replace(/^['"]|['"]$/g, ''))
.filter(Boolean);
const manifestAliases = [...manifest.windsurf].sort();
assert.deepStrictEqual(
[...srcAliases].sort(),
manifestAliases,
`FALLBACK_ALIASES windsurf=${JSON.stringify(srcAliases.sort())} must match manifest windsurf=${JSON.stringify(manifestAliases)}`,
);
});
});