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
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
{
"name": "cursor",
"source": "./plugins/cursor",
"description": "Delegate coding tasks from Claude Code to Cursor CLI (Composer 2 by default).",
"description": "Delegate coding tasks from Claude Code to Cursor CLI (Composer by default).",
"strict": false
}
]
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased

### Fixed

- **Model aliases updated for Composer 2.5.** Cursor retired the Composer 2.x ids — `cursor-agent --list-models` now lists only `composer-2.5` and `composer-2.5-fast` (verified on macOS, 2026-06-10). The `composer`, `composer-fast`, and `fast` shortcuts now resolve to `composer-2.5-fast` (was the dead `composer-2-fast`), and `composer-full` resolves to `composer-2.5` (was `composer-2`). The retired `composer-2` / `composer-2-fast` ids are kept as identity passthroughs so users on older `cursor-agent` builds aren't broken. README, the `cursor-runner` agent guidance, command/package descriptions, and tests updated to match. (#8)

## 0.3.0 — /cursor:review + codebase hardening

### Added
Expand Down
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# cursor-plugin-cc

> **Claude plans. Cursor writes. Claude reviews.**
> A Claude Code plugin that delegates coding _execution_ to Cursor's Composer 2 — without ever leaving the Claude Code TUI.
> A Claude Code plugin that delegates coding _execution_ to Cursor's Composer — without ever leaving the Claude Code TUI.

[![CI](https://github.com/freema/cursor-plugin-cc/actions/workflows/ci.yml/badge.svg)](https://github.com/freema/cursor-plugin-cc/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
Expand All @@ -22,7 +22,7 @@ claude ▸ Plan written to ~/.claude/plans/health-endpoint.md
you ▸ /cursor:from-plan --delegate

plugin ▸ wrote tasks/20260427-1830-health-endpoint.md
▸ handing off to cursor-agent (composer-2-fast, --force)…
▸ handing off to cursor-agent (composer-2.5-fast, --force)…

cursor ▸ ✓ src/routes/health.ts (new, 24 lines)
▸ ✓ src/app.ts (mounted route)
Expand All @@ -42,7 +42,7 @@ cursor ▸ ✓ src/routes/health.ts (1 line changed)

That's the whole loop. Claude does the **thinking** (plan, review). Cursor does the **typing** (file edits, tests). You stay in one TUI.

**Why this is fast:** `composer-2-fast` is Cursor's tuned-for-CLI variant — it ships small, well-scoped changes in seconds. Claude Code spends its tokens on planning and reviewing, where thinking actually matters. Two tools, each doing what they're best at.
**Why this is fast:** `composer-2.5-fast` is Cursor's tuned-for-CLI variant — it ships small, well-scoped changes in seconds. Claude Code spends its tokens on planning and reviewing, where thinking actually matters. Two tools, each doing what they're best at.

## Install

Expand Down Expand Up @@ -73,7 +73,7 @@ The first `/cursor:setup` run tells you if `cursor-agent` is missing or unauthen
### Requirements

- Node.js **≥ 18.18**
- A Cursor account — paid for Composer 2 models; free works with `--model auto` or other entitled models
- A Cursor account — paid for Composer models; free works with `--model auto` or other entitled models
- `cursor-agent` on your `PATH` — install via `curl https://cursor.com/install -fsS | bash`
- `cursor-agent login` completed at least once

Expand Down Expand Up @@ -134,7 +134,7 @@ Plus a `cursor-runner` subagent you can invoke from inside Claude to delegate we

## Why this plugin

Short answer: **Composer 2 is genuinely good at most day-to-day coding work** — and I don't want a pile of terminal windows to drive it. I want Claude Code to be the orchestrator for everything. The flow that keeps working for me is simple: **Claude makes the plan, Composer executes it, Claude reviews the diff.** Two tools, each doing what it is best at.
Short answer: **Composer is genuinely good at most day-to-day coding work** — and I don't want a pile of terminal windows to drive it. I want Claude Code to be the orchestrator for everything. The flow that keeps working for me is simple: **Claude makes the plan, Composer executes it, Claude reviews the diff.** Two tools, each doing what it is best at.

"Why not do the whole thing inside Cursor, then?" Claude Code has a certain magic, particularly around planning. It is not purely about the underlying model — it is the whole rig (long-context sessions, subagents, the TUI, the way tools compose) that, in my experience, only really clicks inside Claude Code.

Expand All @@ -156,7 +156,7 @@ Hand a coding task to `cursor-agent -p …`.

| Flag | Default | Effect |
| ---------------------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--model <id>` | `auto` (or `$CURSOR_PLUGIN_CC_DEFAULT_MODEL`) | Aliases → real Cursor ids: `composer`/`fast` → `composer-2-fast`, `composer-2` → `composer-2`, `sonnet` → `claude-4.6-sonnet-medium`, `opus` → `claude-opus-4-7-high`, `gpt`/`codex` → `gpt-5.3-codex`, `grok` → `grok-4-20`, `gemini` → `gemini-3.1-pro`, `auto` → `auto`. Unknown ids forwarded as-is. `auto` lets Cursor pick whatever model your account is entitled to — safe if you don't have a Composer 2 seat. Run `/cursor:setup --print-models` for the live list. |
| `--model <id>` | `auto` (or `$CURSOR_PLUGIN_CC_DEFAULT_MODEL`) | Aliases → real Cursor ids: `composer`/`fast` → `composer-2.5-fast`, `composer-full` → `composer-2.5`, `sonnet` → `claude-4.6-sonnet-medium`, `opus` → `claude-opus-4-7-high`, `gpt`/`codex` → `gpt-5.3-codex`, `grok` → `grok-4-20`, `gemini` → `gemini-3.1-pro`, `auto` → `auto`. Unknown or retired ids (e.g. `composer-2`, `composer-2-fast`) are forwarded as-is. `auto` lets Cursor pick whatever model your account is entitled to — safe if you don't have a Composer seat. Run `/cursor:setup --print-models` for the live list. |
| `--background` | off | Detach; the command returns a job id immediately. |
| `--wait` | on (if not `--background`) | Block until finished. |
| `--fresh` | off | Start a brand-new Cursor session (no resume). |
Expand Down Expand Up @@ -365,11 +365,11 @@ The plugin codebase is English, but it does not impose a language policy on **yo
The two-phase loop above is the concept; here is the concrete workflow that falls out of it in practice, and the one I keep reaching for:

1. **Plan in Claude Code and write a task file.** Describe what you want; ask Claude to draft a _task spec_ — a markdown file with **goal**, **acceptance criteria**, **files to touch**, and **how to verify** (the same five sections the `cursor-runner` subagent enforces). Save it under `tasks/<slug>.md` in the repo.
2. **Hand the file to Cursor.** Run `/cursor:delegate @tasks/<slug>.md implement this`. The `@path` shorthand inlines the file contents into the prompt, so Cursor gets the full spec without Claude having to re-type it. Composer 2 executes — it is genuinely fast, and a precisely defined task is usually a one-shot job.
2. **Hand the file to Cursor.** Run `/cursor:delegate @tasks/<slug>.md implement this`. The `@path` shorthand inlines the file contents into the prompt, so Cursor gets the full spec without Claude having to re-type it. Composer executes — it is genuinely fast, and a precisely defined task is usually a one-shot job.
3. **Back in Claude Code: review the diff.** Approve, or iterate with `/cursor:resume "fix X"` on the same thread, or escalate with `/cursor:delegate --model opus --fresh <same task file>` if Composer stalled.
4. **Keep the task files around.** `tasks/` becomes a little log of what the repo's delegated changes looked like. Handy when you want to re-delegate a similar slice — just copy an old file, tweak the bullets.

Why this works: Claude's tokens go into **planning and reviewing**, where thinking matters; Cursor's Composer 2 handles **the actual typing**, where it is cheaper and faster. The task file is the contract between the two phases — if it is sloppy, no model will save you. Writing a good one is itself a skill, and it is the only skill this workflow asks of you.
Why this works: Claude's tokens go into **planning and reviewing**, where thinking matters; Cursor's Composer handles **the actual typing**, where it is cheaper and faster. The task file is the contract between the two phases — if it is sloppy, no model will save you. Writing a good one is itself a skill, and it is the only skill this workflow asks of you.

If you want Claude to do the whole thing automatically — draft the task file AND hand it off — that is what the `cursor-runner` subagent is for. Ask it to either "draft a task file for X" (it stops there) or "implement X via Cursor" (it drafts, hands off, and reports the diff).

Expand Down
10 changes: 5 additions & 5 deletions plugins/cursor/agents/cursor-runner.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: cursor-runner
description: Hand off a well-specified coding task to the Cursor CLI (`cursor-agent`) via `/cursor:delegate`. Use for small-to-medium, well-scoped changes where speed matters (default model `composer-2-fast`). Do NOT use this agent for code review, design decisions, or large refactors — those stay with the main Claude conversation.
description: Hand off a well-specified coding task to the Cursor CLI (`cursor-agent`) via `/cursor:delegate`. Use for small-to-medium, well-scoped changes where speed matters (default model `composer-2.5-fast`). Do NOT use this agent for code review, design decisions, or large refactors — those stay with the main Claude conversation.
tools: [Bash, Read]
---

Expand Down Expand Up @@ -56,9 +56,9 @@ Small slices give Cursor a tight scope, make the diff reviewable, and make failu

### 4. Pick a model

Default is `composer-2-fast` — Cursor's own current default and the fastest Composer variant. Escalate only when the task warrants it:
Default is `composer-2.5-fast` — Cursor's own current default and the fastest Composer variant. Escalate only when the task warrants it:

- `composer-2` (non-fast) — quality matters slightly more than latency, but the task is still well-scoped.
- `composer-2.5` (non-fast) — quality matters slightly more than latency, but the task is still well-scoped.
- `sonnet` (`claude-4.6-sonnet-medium`) — more than ~5 files touched, or moderate architecture changes.
- `opus` (`claude-opus-4-7-high`) — cross-cutting refactor, subtle correctness, or a prior `composer` run failed.
- `gpt` / `codex` (`gpt-5.3-codex`) — only when the user explicitly asks for it.
Expand Down Expand Up @@ -92,13 +92,13 @@ Do not paraphrase the summary, do not rewrite the file list, do not hide the cha
- **Do not edit files yourself.** Use `Read` only to ground the prompt you send to Cursor — never to patch code directly.
- **Do not review Cursor's diff.** Review is the main Claude conversation's job. Your job ends when you hand back Cursor's report.
- **Do not run `/cursor:status`, `/cursor:result`, or `/cursor:cancel` on your own.** If the main conversation wants them, it will run them itself.
- **Do not escalate models without a reason.** `composer-2-fast` is the default for a reason (speed + cost). Escalate only when the task description itself warrants it.
- **Do not escalate models without a reason.** `composer-2.5-fast` is the default for a reason (speed + cost). Escalate only when the task description itself warrants it.
- **Do not impose a language policy on the target repo.** Follow whatever conventions the target repo's `AGENTS.md` / `.cursor/rules` / existing code already establishes.

## Output format

Return exactly what `delegate.ts` prints. One line of your own framing is fine:

> Delegated to Cursor (`composer-2-fast`). Result below.
> Delegated to Cursor (`composer-2.5-fast`). Result below.

Then Cursor's block, unedited.
2 changes: 1 addition & 1 deletion plugins/cursor/commands/delegate.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
description: Delegate a coding task to the Cursor CLI agent (Composer 2 by default).
description: Delegate a coding task to the Cursor CLI agent (Composer by default).
argument-hint: '[--background] [--wait] [--fresh] [--resume[=chat-id]] [--model <id>] [--cloud] [--no-force] [--timeout <sec>] <task...>'
allowed-tools: Bash(node:*)
---
Expand Down
4 changes: 2 additions & 2 deletions plugins/cursor/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "cursor-plugin-cc",
"version": "0.3.0",
"description": "Use Cursor CLI from Claude Code to delegate coding tasks to Composer 2 and other Cursor models.",
"description": "Use Cursor CLI from Claude Code to delegate coding tasks to Composer and other Cursor models.",
"type": "module",
"license": "MIT",
"author": {
Expand All @@ -20,7 +20,7 @@
"cursor",
"cursor-cli",
"cursor-agent",
"composer-2",
"composer-2.5",
"ai-coding",
"agent-delegation"
],
Expand Down
2 changes: 1 addition & 1 deletion plugins/cursor/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "cursor",
"version": "0.3.0",
"description": "Hand off tasks from Claude Code to cursor-agent. Composer 2 optimised.",
"description": "Hand off tasks from Claude Code to cursor-agent. Composer-optimised.",
"author": {
"name": "Tomas Grasl",
"url": "https://www.tomasgrasl.cz/"
Expand Down
15 changes: 10 additions & 5 deletions plugins/cursor/scripts/lib/cursor.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,17 @@ import { run } from './run.mjs';
// rotates these over time — `/cursor:setup --print-models` shows the live
// list for the current account. Unknown ids are passed through verbatim.
export const MODEL_ALIASES = {
composer: 'composer-2-fast',
'composer-fast': 'composer-2-fast',
fast: 'composer-2-fast',
// Short shortcuts point at Cursor's current Composer line (2.5).
composer: 'composer-2.5-fast',
'composer-fast': 'composer-2.5-fast',
fast: 'composer-2.5-fast',
'composer-full': 'composer-2.5',
// Current Composer ids (identity — also documents the live names).
'composer-2.5-fast': 'composer-2.5-fast',
'composer-2.5': 'composer-2.5',
// Retired Composer ids kept as passthrough for older cursor-agent builds.
'composer-2-fast': 'composer-2-fast',
'composer-2': 'composer-2',
'composer-full': 'composer-2',
'composer-1.5': 'composer-1.5',
auto: 'auto',
sonnet: 'claude-4.6-sonnet-medium',
Expand Down Expand Up @@ -42,7 +47,7 @@ export const MODEL_ALIASES = {
};

// `auto` lets Cursor pick whatever model the account is entitled to —
// safe default for users without a paid `composer-2-fast` seat. Power users
// safe default for users without a paid Composer seat. Power users
// can override per-invocation via `--model <id>` or globally via the env var
// CURSOR_PLUGIN_CC_DEFAULT_MODEL.
export const DEFAULT_MODEL = 'auto';
Expand Down
22 changes: 15 additions & 7 deletions plugins/cursor/tests/cursor.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import { HAPPY_FIXTURE, STUB_BIN, makeTempHome } from './helpers.mjs';

describe('buildArgs', () => {
it('includes the expected flags by default', () => {
const args = buildArgs({ prompt: 'hi', model: 'composer-2' });
const args = buildArgs({ prompt: 'hi', model: 'composer-2.5' });
expect(args).toContain('-p');
expect(args).toContain('--output-format');
expect(args).toContain('stream-json');
expect(args).toContain('--trust');
expect(args).toContain('--model');
expect(args).toContain('composer-2');
expect(args).toContain('composer-2.5');
expect(args.at(-1)).toBe('hi');
});

Expand Down Expand Up @@ -51,16 +51,24 @@ describe('resolveModel', () => {
});

it('maps aliases to real Cursor ids', () => {
expect(resolveModel('composer')).toBe('composer-2-fast');
expect(resolveModel('fast')).toBe('composer-2-fast');
expect(resolveModel('composer-2')).toBe('composer-2');
expect(resolveModel('composer')).toBe('composer-2.5-fast');
expect(resolveModel('composer-fast')).toBe('composer-2.5-fast');
expect(resolveModel('fast')).toBe('composer-2.5-fast');
expect(resolveModel('composer-full')).toBe('composer-2.5');
expect(resolveModel('composer-2.5')).toBe('composer-2.5');
expect(resolveModel('composer-2.5-fast')).toBe('composer-2.5-fast');
expect(resolveModel('sonnet')).toBe('claude-4.6-sonnet-medium');
expect(resolveModel('opus')).toBe('claude-opus-4-7-high');
expect(resolveModel('gpt')).toBe('gpt-5.3-codex');
expect(resolveModel('grok')).toBe('grok-4-20');
expect(resolveModel('gemini')).toBe('gemini-3.1-pro');
});

it('keeps retired Composer ids as passthrough for older cursor-agent builds', () => {
expect(resolveModel('composer-2')).toBe('composer-2');
expect(resolveModel('composer-2-fast')).toBe('composer-2-fast');
});

it('defaults to auto when empty (no env override)', () => {
delete process.env.CURSOR_PLUGIN_CC_DEFAULT_MODEL;
expect(resolveModel(undefined)).toBe('auto');
Expand All @@ -69,7 +77,7 @@ describe('resolveModel', () => {

it('honours CURSOR_PLUGIN_CC_DEFAULT_MODEL when no input is given', () => {
process.env.CURSOR_PLUGIN_CC_DEFAULT_MODEL = 'composer';
expect(resolveModel(undefined)).toBe('composer-2-fast');
expect(resolveModel(undefined)).toBe('composer-2.5-fast');
process.env.CURSOR_PLUGIN_CC_DEFAULT_MODEL = 'some-custom-id';
expect(resolveModel('')).toBe('some-custom-id');
});
Expand Down Expand Up @@ -107,7 +115,7 @@ describe('runHeadless against stub binary', () => {
const logPath = `${tmp.dir}/run.ndjson`;
const result = await runHeadless({
prompt: 'hi',
model: 'composer-2',
model: 'composer-2.5',
force: false,
logPath,
timeoutSec: 10,
Expand Down
2 changes: 1 addition & 1 deletion plugins/cursor/tests/delegate.fg.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ describe('delegate foreground', () => {
expect(jobs.length).toBe(1);
const job = jobs[0];
expect(job.status).toBe('done');
expect(job.model).toBe('composer-2-fast');
expect(job.model).toBe('composer-2.5-fast');
expect(job.cursorChatId).toBe('chat_abc123');
expect(job.filesTouched?.length ?? 0).toBeGreaterThan(0);
});
Expand Down
2 changes: 1 addition & 1 deletion plugins/cursor/tests/jobs.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ describe('jobs registry', () => {
});

it('creates, reads, and updates a job atomically', () => {
const job = createJob({ id: 'job1', repoPath: repo, prompt: 'do it', model: 'composer-2' });
const job = createJob({ id: 'job1', repoPath: repo, prompt: 'do it', model: 'composer-2.5' });
expect(job.status).toBe('running');
const read = readJob(repo, 'job1');
expect(read?.prompt).toBe('do it');
Expand Down
Loading