From a4608306a653ca3b88336fb0a40eee7c028db843 Mon Sep 17 00:00:00 2001 From: aditya mer Date: Sun, 21 Jun 2026 16:56:07 +0530 Subject: [PATCH 1/2] fix(cursor): default binary path to cursor-agent (#3479) When Grok CLI is installed alongside Cursor, the shared `agent` command name resolves to Grok and breaks Cursor ACP discovery. Default Cursor to `cursor-agent`, map legacy `agent` configs on decode, and align update probes and spawn fallbacks with the new binary name. --- .../cursor-acp-model-mismatch-probe.ts | 2 +- .../src/provider/Drivers/CursorDriver.ts | 2 +- .../provider/Layers/CursorProvider.test.ts | 2 +- .../ProviderInstanceRegistryLive.test.ts | 2 +- .../provider/Layers/ProviderRegistry.test.ts | 2 +- .../provider/acp/CursorAcpCliProbe.test.ts | 6 ++--- .../src/provider/acp/CursorAcpSupport.test.ts | 2 +- .../src/provider/acp/CursorAcpSupport.ts | 8 ++++-- .../providerMaintenanceRunner.test.ts | 4 +-- packages/contracts/src/settings.test.ts | 25 ++++++++++++++++++- packages/contracts/src/settings.ts | 23 +++++++++++++++-- 11 files changed, 62 insertions(+), 16 deletions(-) diff --git a/apps/server/scripts/cursor-acp-model-mismatch-probe.ts b/apps/server/scripts/cursor-acp-model-mismatch-probe.ts index b36c2b2d496..7e4e88aeb2b 100644 --- a/apps/server/scripts/cursor-acp-model-mismatch-probe.ts +++ b/apps/server/scripts/cursor-acp-model-mismatch-probe.ts @@ -62,7 +62,7 @@ const promptText = NodeProcess.argv[4] ?? "helo"; const targetReasoning = NodeProcess.env.CURSOR_REASONING ?? ""; const targetContext = NodeProcess.env.CURSOR_CONTEXT ?? ""; const targetFast = NodeProcess.env.CURSOR_FAST ?? ""; -const agentBin = NodeProcess.env.CURSOR_AGENT_BIN ?? "agent"; +const agentBin = NodeProcess.env.CURSOR_AGENT_BIN ?? "cursor-agent"; const promptWaitMs = Number(NodeProcess.env.CURSOR_PROMPT_WAIT_MS ?? "4000"); const requestTimeoutMs = Number(NodeProcess.env.CURSOR_REQUEST_TIMEOUT_MS ?? "20000"); diff --git a/apps/server/src/provider/Drivers/CursorDriver.ts b/apps/server/src/provider/Drivers/CursorDriver.ts index c394a7d1b43..60a370ea80c 100644 --- a/apps/server/src/provider/Drivers/CursorDriver.ts +++ b/apps/server/src/provider/Drivers/CursorDriver.ts @@ -58,7 +58,7 @@ const UPDATE = makeStaticProviderMaintenanceResolver( makeProviderMaintenanceCapabilities({ provider: DRIVER_KIND, packageName: null, - updateExecutable: "agent", + updateExecutable: "cursor-agent", updateArgs: ["update"], updateLockKey: "cursor-agent", }), diff --git a/apps/server/src/provider/Layers/CursorProvider.test.ts b/apps/server/src/provider/Layers/CursorProvider.test.ts index 60a7312eea3..738871baf08 100644 --- a/apps/server/src/provider/Layers/CursorProvider.test.ts +++ b/apps/server/src/provider/Layers/CursorProvider.test.ts @@ -289,7 +289,7 @@ const parameterizedClaudeModelOptionConfigOptions = [ const baseCursorSettings: CursorSettings = { enabled: true, - binaryPath: "agent", + binaryPath: "cursor-agent", apiEndpoint: "", customModels: [], }; diff --git a/apps/server/src/provider/Layers/ProviderInstanceRegistryLive.test.ts b/apps/server/src/provider/Layers/ProviderInstanceRegistryLive.test.ts index dbfa7faffea..73390450efa 100644 --- a/apps/server/src/provider/Layers/ProviderInstanceRegistryLive.test.ts +++ b/apps/server/src/provider/Layers/ProviderInstanceRegistryLive.test.ts @@ -76,7 +76,7 @@ const makeClaudeConfig = (overrides: Partial): ClaudeSettings => const makeCursorConfig = (overrides: Partial): CursorSettings => ({ enabled: false, - binaryPath: "agent", + binaryPath: "cursor-agent", apiEndpoint: "", customModels: [], ...overrides, diff --git a/apps/server/src/provider/Layers/ProviderRegistry.test.ts b/apps/server/src/provider/Layers/ProviderRegistry.test.ts index b3ab1145495..9a46d8a8056 100644 --- a/apps/server/src/provider/Layers/ProviderRegistry.test.ts +++ b/apps/server/src/provider/Layers/ProviderRegistry.test.ts @@ -1396,7 +1396,7 @@ it.layer(Layer.mergeAll(NodeServices.layer, ServerSettingsModule.layerTest(), Te Layer.provideMerge(OpenCodeRuntime.OpenCodeRuntimeLive), Layer.provideMerge( mockCommandSpawnerLayer((command, args) => { - if (command === "agent") { + if (command === "cursor-agent") { cursorSpawned = true; } const joined = args.join(" "); diff --git a/apps/server/src/provider/acp/CursorAcpCliProbe.test.ts b/apps/server/src/provider/acp/CursorAcpCliProbe.test.ts index eebe5ddd92e..ae2e7231b36 100644 --- a/apps/server/src/provider/acp/CursorAcpCliProbe.test.ts +++ b/apps/server/src/provider/acp/CursorAcpCliProbe.test.ts @@ -21,7 +21,7 @@ describe.runIf(process.env.T3_CURSOR_ACP_PROBE === "1")("Cursor ACP CLI probe", Effect.provide( AcpSessionRuntime.layer({ spawn: { - command: "agent", + command: "cursor-agent", args: ["acp"], cwd: process.cwd(), }, @@ -77,7 +77,7 @@ describe.runIf(process.env.T3_CURSOR_ACP_PROBE === "1")("Cursor ACP CLI probe", AcpSessionRuntime.layer({ authMethodId: "cursor_login", spawn: { - command: "agent", + command: "cursor-agent", args: ["acp"], cwd: process.cwd(), }, @@ -133,7 +133,7 @@ describe.runIf(process.env.T3_CURSOR_ACP_PROBE === "1")("Cursor ACP CLI probe", AcpSessionRuntime.layer({ authMethodId: "cursor_login", spawn: { - command: "agent", + command: "cursor-agent", args: ["acp"], cwd: process.cwd(), }, diff --git a/apps/server/src/provider/acp/CursorAcpSupport.test.ts b/apps/server/src/provider/acp/CursorAcpSupport.test.ts index 78bc16c9f3b..a095fdd679b 100644 --- a/apps/server/src/provider/acp/CursorAcpSupport.test.ts +++ b/apps/server/src/provider/acp/CursorAcpSupport.test.ts @@ -53,7 +53,7 @@ const parameterizedGpt54ConfigOptions: ReadonlyArray { it("builds the default Cursor ACP command", () => { expect(buildCursorAcpSpawnInput(undefined, "/tmp/project")).toEqual({ - command: "agent", + command: "cursor-agent", args: ["acp"], cwd: "/tmp/project", }); diff --git a/apps/server/src/provider/acp/CursorAcpSupport.ts b/apps/server/src/provider/acp/CursorAcpSupport.ts index 169d7c6206d..7d14040576d 100644 --- a/apps/server/src/provider/acp/CursorAcpSupport.ts +++ b/apps/server/src/provider/acp/CursorAcpSupport.ts @@ -1,4 +1,8 @@ -import { type CursorSettings, type ProviderOptionSelection } from "@t3tools/contracts"; +import { + DEFAULT_CURSOR_BINARY_PATH, + type CursorSettings, + type ProviderOptionSelection, +} from "@t3tools/contracts"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Scope from "effect/Scope"; @@ -35,7 +39,7 @@ export function buildCursorAcpSpawnInput( environment?: NodeJS.ProcessEnv, ): AcpSessionRuntime.AcpSpawnInput { return { - command: cursorSettings?.binaryPath || "agent", + command: cursorSettings?.binaryPath || DEFAULT_CURSOR_BINARY_PATH, args: [ ...(cursorSettings?.apiEndpoint ? (["-e", cursorSettings.apiEndpoint] as const) : []), "acp", diff --git a/apps/server/src/provider/providerMaintenanceRunner.test.ts b/apps/server/src/provider/providerMaintenanceRunner.test.ts index 5ffb69cd5f7..657821f2756 100644 --- a/apps/server/src/provider/providerMaintenanceRunner.test.ts +++ b/apps/server/src/provider/providerMaintenanceRunner.test.ts @@ -40,7 +40,7 @@ function lifecycleFor(provider: ProviderDriverKind): ProviderMaintenanceCapabili return makeProviderMaintenanceCapabilities({ provider, packageName: null, - updateExecutable: "agent", + updateExecutable: "cursor-agent", updateArgs: ["update"], updateLockKey: "cursor-agent", }); @@ -218,7 +218,7 @@ describe("providerMaintenanceRunner", () => { const result = yield* updater.updateProvider(CURSOR_DRIVER); assert.deepStrictEqual(calls, [ { - command: "agent", + command: "cursor-agent", args: ["update"], }, ]); diff --git a/packages/contracts/src/settings.test.ts b/packages/contracts/src/settings.test.ts index aba97cbe205..fd483abf6bf 100644 --- a/packages/contracts/src/settings.test.ts +++ b/packages/contracts/src/settings.test.ts @@ -2,7 +2,12 @@ import { describe, expect, it } from "vite-plus/test"; import * as Schema from "effect/Schema"; import { ProviderInstanceId } from "./providerInstance.ts"; -import { DEFAULT_SERVER_SETTINGS, ServerSettings, ServerSettingsPatch } from "./settings.ts"; +import { + CursorSettings, + DEFAULT_SERVER_SETTINGS, + ServerSettings, + ServerSettingsPatch, +} from "./settings.ts"; const decodeServerSettings = Schema.decodeUnknownSync(ServerSettings); const decodeServerSettingsPatch = Schema.decodeUnknownSync(ServerSettingsPatch); @@ -106,6 +111,24 @@ describe("ServerSettingsPatch.providerInstances", () => { }); }); +describe("CursorSettings binary path", () => { + const decodeCursorSettings = Schema.decodeUnknownSync(CursorSettings); + + it("defaults to cursor-agent", () => { + expect(decodeCursorSettings({}).binaryPath).toBe("cursor-agent"); + }); + + it("maps the legacy agent binary name to cursor-agent", () => { + expect(decodeCursorSettings({ binaryPath: "agent" }).binaryPath).toBe("cursor-agent"); + }); + + it("preserves explicit binary paths", () => { + expect(decodeCursorSettings({ binaryPath: "/usr/local/bin/agent" }).binaryPath).toBe( + "/usr/local/bin/agent", + ); + }); +}); + describe("ServerSettingsPatch string normalization", () => { it("trims string settings while decoding patches", () => { const patch = decodeServerSettingsPatch({ diff --git a/packages/contracts/src/settings.ts b/packages/contracts/src/settings.ts index 7ba267b1e72..0bae190bbfa 100644 --- a/packages/contracts/src/settings.ts +++ b/packages/contracts/src/settings.ts @@ -114,6 +114,25 @@ const makeBinaryPathSetting = (fallback: string) => Schema.withDecodingDefault(Effect.succeed(fallback)), ); +export const LEGACY_CURSOR_BINARY_PATH = "agent"; +export const DEFAULT_CURSOR_BINARY_PATH = "cursor-agent"; + +export const normalizeCursorBinaryPath = (value: string): string => + value === LEGACY_CURSOR_BINARY_PATH ? DEFAULT_CURSOR_BINARY_PATH : value; + +const makeCursorBinaryPathSetting = () => + TrimmedString.pipe( + Schema.decodeTo( + Schema.String, + SchemaTransformation.transformOrFail({ + decode: (value) => + Effect.succeed(normalizeCursorBinaryPath(value || DEFAULT_CURSOR_BINARY_PATH)), + encode: (value) => Effect.succeed(value), + }), + ), + Schema.withDecodingDefault(Effect.succeed(DEFAULT_CURSOR_BINARY_PATH)), + ); + export type ProviderSettingsFormControl = "text" | "password" | "textarea" | "switch"; export interface ProviderSettingsFormAnnotation { @@ -252,11 +271,11 @@ export const CursorSettings = makeProviderSettingsSchema( Schema.withDecodingDefault(Effect.succeed(false)), Schema.annotateKey({ providerSettingsForm: { hidden: true } }), ), - binaryPath: makeBinaryPathSetting("agent").pipe( + binaryPath: makeCursorBinaryPathSetting().pipe( Schema.annotateKey({ title: "Binary path", description: "Path to the Cursor agent binary.", - providerSettingsForm: { placeholder: "agent", clearWhenEmpty: "omit" }, + providerSettingsForm: { placeholder: "cursor-agent", clearWhenEmpty: "omit" }, }), ), apiEndpoint: TrimmedString.pipe( From e9d51bf98109ff871b087a5a6123c749e579dd21 Mon Sep 17 00:00:00 2001 From: aditya mer Date: Sun, 21 Jun 2026 17:02:43 +0530 Subject: [PATCH 2/2] refactor(cursor): use literal cursor-agent fallback in ACP spawn --- apps/server/src/provider/acp/CursorAcpSupport.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/apps/server/src/provider/acp/CursorAcpSupport.ts b/apps/server/src/provider/acp/CursorAcpSupport.ts index 7d14040576d..c5c16b1023e 100644 --- a/apps/server/src/provider/acp/CursorAcpSupport.ts +++ b/apps/server/src/provider/acp/CursorAcpSupport.ts @@ -1,8 +1,4 @@ -import { - DEFAULT_CURSOR_BINARY_PATH, - type CursorSettings, - type ProviderOptionSelection, -} from "@t3tools/contracts"; +import { type CursorSettings, type ProviderOptionSelection } from "@t3tools/contracts"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Scope from "effect/Scope"; @@ -39,7 +35,7 @@ export function buildCursorAcpSpawnInput( environment?: NodeJS.ProcessEnv, ): AcpSessionRuntime.AcpSpawnInput { return { - command: cursorSettings?.binaryPath || DEFAULT_CURSOR_BINARY_PATH, + command: cursorSettings?.binaryPath || "cursor-agent", args: [ ...(cursorSettings?.apiEndpoint ? (["-e", cursorSettings.apiEndpoint] as const) : []), "acp",