import { CursorSettings, ProviderInstanceId } from "@t3tools/contracts";import { describe, expect, it } from "@effect/vitest";import * as Effect from "effect/Effect";import * as Schema from "effect/Schema";import { createModelSelection } from "@t3tools/shared/model";import { beforeEach, vi } from "vite-plus/test"; import { makeCursorTextGeneration } from "./CursorTextGeneration.ts"; const cursorSdkMock = vi.hoisted(() => ({ prompt: vi.fn(async () => ({ id: "run-cursor-text-generation-test", status: "finished" as const, result: '{"subject":"Add generated commit message","body":"- verify cursor sdk text generation"}', })),})); vi.mock("@cursor/sdk", () => ({ Agent: { prompt: cursorSdkMock.prompt, },})); const decodeCursorSettings = Schema.decodeSync(CursorSettings);const cursorSettings = decodeCursorSettings({ enabled: true }); beforeEach(() => { cursorSdkMock.prompt.mockReset(); cursorSdkMock.prompt.mockResolvedValue({ id: "run-cursor-text-generation-test", status: "finished", result: '{"subject":"Add generated commit message","body":"- verify cursor sdk text generation"}', });}); describe("CursorTextGeneration", () => { it.effect("uses the Cursor SDK prompt API with model parameters and API key", () => Effect.gen(function* () { const textGeneration = yield* makeCursorTextGeneration(cursorSettings, { CURSOR_API_KEY: "test-cursor-key", }); const generated = yield* textGeneration.generateCommitMessage({ cwd: process.cwd(), branch: "feature/cursor-text-generation", stagedSummary: "M apps/server/src/textGeneration/CursorTextGeneration.ts", stagedPatch: "diff --git a/apps/server/src/textGeneration/CursorTextGeneration.ts b/apps/server/src/textGeneration/CursorTextGeneration.ts", modelSelection: createModelSelection(ProviderInstanceId.make("cursor"), "gpt-5.4", [ { id: "thinking", value: "high" }, { id: "contextWindow", value: "1m" }, { id: "fastMode", value: true }, ]), }); expect(generated.subject).toBe("Add generated commit message"); expect(generated.body).toBe("- verify cursor sdk text generation"); expect(cursorSdkMock.prompt).toHaveBeenCalledTimes(1); const [prompt, options] = ( cursorSdkMock.prompt.mock.calls as unknown as Array<[string, unknown]> )[0]!; expect(prompt).toContain("Staged patch:"); expect(options).toEqual({ apiKey: "test-cursor-key", mode: "agent", model: { id: "gpt-5.4", params: [ { id: "thinking", value: "high" }, { id: "context", value: "1m" }, { id: "fast", value: "true" }, ], }, local: { cwd: process.cwd(), autoReview: false, sandboxOptions: { enabled: false }, enableAgentRetries: true, }, }); }), ); it.effect("accepts json objects with extra assistant text around them", () => Effect.gen(function* () { cursorSdkMock.prompt.mockResolvedValueOnce({ id: "run-cursor-text-generation-test", status: "finished", result: 'Sure, here is the JSON:\n```json\n{\n "subject": "Update README dummy comment with attribution and date",\n "body": ""\n}\n```\nDone.', }); const textGeneration = yield* makeCursorTextGeneration(cursorSettings, { CURSOR_API_KEY: "test-cursor-key", }); const generated = yield* textGeneration.generateCommitMessage({ cwd: process.cwd(), branch: "feature/cursor-noisy-json", stagedSummary: "M README.md", stagedPatch: "diff --git a/README.md b/README.md", modelSelection: { instanceId: ProviderInstanceId.make("cursor"), model: "composer-2", }, }); expect(generated.subject).toBe("Update README dummy comment with attribution and date"); expect(generated.body).toBe(""); }), ); it.effect("generates thread titles through Cursor SDK text generation", () => Effect.gen(function* () { cursorSdkMock.prompt.mockResolvedValueOnce({ id: "run-cursor-title-generation-test", status: "finished", result: '{"title":"\\"Trim reconnect spinner status after resume.\\""}', }); const textGeneration = yield* makeCursorTextGeneration(cursorSettings, { CURSOR_API_KEY: "test-cursor-key", }); const generated = yield* textGeneration.generateThreadTitle({ cwd: process.cwd(), message: "Fix the reconnect spinner after a resumed session.", modelSelection: { instanceId: ProviderInstanceId.make("cursor"), model: "composer-2", }, }); expect(generated.title).toBe("Trim reconnect spinner status after resume."); }), ); it.effect("requires CURSOR_API_KEY before calling the SDK", () => Effect.gen(function* () { const textGeneration = yield* makeCursorTextGeneration(cursorSettings, {}); const error = yield* Effect.flip( textGeneration.generateCommitMessage({ cwd: process.cwd(), branch: "feature/cursor-api-key", stagedSummary: "M README.md", stagedPatch: "diff --git a/README.md b/README.md", modelSelection: { instanceId: ProviderInstanceId.make("cursor"), model: "composer-2", }, }), ); expect(error.detail).toBe( "Cursor API key is required. Add CURSOR_API_KEY in provider settings.", ); expect(cursorSdkMock.prompt).not.toHaveBeenCalled(); }), );});
import { CursorSettings, ProviderInstanceId } from "@t3tools/contracts";
import { describe, expect, it } from "@effect/vitest";
import * as Effect from "effect/Effect";
import * as Schema from "effect/Schema";
import { createModelSelection } from "@t3tools/shared/model";
import { beforeEach, vi } from "vite-plus/test";
import { makeCursorTextGeneration } from "./CursorTextGeneration.ts";
const cursorSdkMock = vi.hoisted(() => ({
prompt: vi.fn(async () => ({
id: "run-cursor-text-generation-test",
status: "finished" as const,
result:
'{"subject":"Add generated commit message","body":"- verify cursor sdk text generation"}',
})),
}));
vi.mock("@cursor/sdk", () => ({
Agent: {
prompt: cursorSdkMock.prompt,
},
}));
const decodeCursorSettings = Schema.decodeSync(CursorSettings);
const cursorSettings = decodeCursorSettings({ enabled: true });
beforeEach(() => {
cursorSdkMock.prompt.mockReset();
cursorSdkMock.prompt.mockResolvedValue({
id: "run-cursor-text-generation-test",
status: "finished",
result:
'{"subject":"Add generated commit message","body":"- verify cursor sdk text generation"}',
});
});
describe("CursorTextGeneration", () => {
it.effect("uses the Cursor SDK prompt API with model parameters and API key", () =>
Effect.gen(function* () {
const textGeneration = yield* makeCursorTextGeneration(cursorSettings, {
CURSOR_API_KEY: "test-cursor-key",
});
const generated = yield* textGeneration.generateCommitMessage({
cwd: process.cwd(),
branch: "feature/cursor-text-generation",
stagedSummary: "M apps/server/src/textGeneration/CursorTextGeneration.ts",
stagedPatch:
"diff --git a/apps/server/src/textGeneration/CursorTextGeneration.ts b/apps/server/src/textGeneration/CursorTextGeneration.ts",
modelSelection: createModelSelection(ProviderInstanceId.make("cursor"), "gpt-5.4", [
{ id: "thinking", value: "high" },
{ id: "contextWindow", value: "1m" },
{ id: "fastMode", value: true },
]),
});
expect(generated.subject).toBe("Add generated commit message");
expect(generated.body).toBe("- verify cursor sdk text generation");
expect(cursorSdkMock.prompt).toHaveBeenCalledTimes(1);
const [prompt, options] = (
cursorSdkMock.prompt.mock.calls as unknown as Array<[string, unknown]>
)[0]!;
expect(prompt).toContain("Staged patch:");
expect(options).toEqual({
apiKey: "test-cursor-key",
mode: "agent",
model: {
id: "gpt-5.4",
params: [
{ id: "thinking", value: "high" },
{ id: "context", value: "1m" },
{ id: "fast", value: "true" },
],
},
local: {
cwd: process.cwd(),
autoReview: false,
sandboxOptions: { enabled: false },
enableAgentRetries: true,
},
});
}),
);
it.effect("accepts json objects with extra assistant text around them", () =>
Effect.gen(function* () {
cursorSdkMock.prompt.mockResolvedValueOnce({
id: "run-cursor-text-generation-test",
status: "finished",
result:
'Sure, here is the JSON:\n```json\n{\n "subject": "Update README dummy comment with attribution and date",\n "body": ""\n}\n```\nDone.',
});
const textGeneration = yield* makeCursorTextGeneration(cursorSettings, {
CURSOR_API_KEY: "test-cursor-key",
});
const generated = yield* textGeneration.generateCommitMessage({
cwd: process.cwd(),
branch: "feature/cursor-noisy-json",
stagedSummary: "M README.md",
stagedPatch: "diff --git a/README.md b/README.md",
modelSelection: {
instanceId: ProviderInstanceId.make("cursor"),
model: "composer-2",
},
});
expect(generated.subject).toBe("Update README dummy comment with attribution and date");
expect(generated.body).toBe("");
}),
);
it.effect("generates thread titles through Cursor SDK text generation", () =>
Effect.gen(function* () {
cursorSdkMock.prompt.mockResolvedValueOnce({
id: "run-cursor-title-generation-test",
status: "finished",
result: '{"title":"\\"Trim reconnect spinner status after resume.\\""}',
});
const textGeneration = yield* makeCursorTextGeneration(cursorSettings, {
CURSOR_API_KEY: "test-cursor-key",
});
const generated = yield* textGeneration.generateThreadTitle({
cwd: process.cwd(),
message: "Fix the reconnect spinner after a resumed session.",
modelSelection: {
instanceId: ProviderInstanceId.make("cursor"),
model: "composer-2",
},
});
expect(generated.title).toBe("Trim reconnect spinner status after resume.");
}),
);
it.effect("requires CURSOR_API_KEY before calling the SDK", () =>
Effect.gen(function* () {
const textGeneration = yield* makeCursorTextGeneration(cursorSettings, {});
const error = yield* Effect.flip(
textGeneration.generateCommitMessage({
cwd: process.cwd(),
branch: "feature/cursor-api-key",
stagedSummary: "M README.md",
stagedPatch: "diff --git a/README.md b/README.md",
modelSelection: {
instanceId: ProviderInstanceId.make("cursor"),
model: "composer-2",
},
}),
);
expect(error.detail).toBe(
"Cursor API key is required. Add CURSOR_API_KEY in provider settings.",
);
expect(cursorSdkMock.prompt).not.toHaveBeenCalled();
}),
);
});