Repository navigation
feat(agents): Cursor Agent CLI as a builtin agent - #1128
Merged
Merged
Conversation
Launches cursor-agent; a prompt rides behind the agent subcommand because the CLI ignores -- for subcommand dispatch (measured on 2026.09.23). Joins no capability list yet: spawn, terminal title and process status.
Monochrome mark from @lobehub/icons-static-svg 1.95.1 (MIT), inlined in currentColor like grok and copilot so it follows the theme in menus, node headers, the RUNNING badge and the notch HUD.
Flags go before the agent subcommand (measured: root options reach the session from either side). plan -> --mode plan, bypass -> --force; auto and acceptEdits emit nothing so the default never widens. Cursor and grok share OWN_MODEL_CATALOGUE so gateway models and the gateway default model never reach them.
Joins AGENT_HOOK_TARGETS: normalizeCursor (conversation_id recorded as the session id), a merge installer for the shared ~/.cursor/hooks.json, a silent gate-safe hook command, fixtures captured from cursor-agent 2026.09.28 and docs/cursor-agent.md with the device checklist.
Resolved: kept the measured flag-placement comment, merged both doc halves, and unified the two cursor capability test blocks into one.
…sfer and title Reads cursor-agent's SQLite chat store with node:sqlite (read-only, located strictly by chat id, no cwd fallback). Joins CHAT_CAPABLE, CHAT_LOCAL_ONLY, CONTEXT_LINK_CAPABLE, TRANSFER_SOURCE_CAPABLE and TITLE_READ_CAPABLE. SSH-remote nodes degrade honestly and never read the local disk. Fixtures are synthesized; an opt-in real-store test prints counts only.
…rt, model catalogue
Measured live in the dev app: a single read 1.5 s after preToolUse found no dialog yet, so a node waiting on Cursor's approval stayed RUNNING. Read again at 4 s and 10 s while the call is still pending; blocked is emitted once, and a long approved command costs at most three reads.
A bare cursor-agent asks before every command (allowlist default), which made a new Cursor node prompt for each task. Map nodeterm's Auto (the default) to Cursor's Auto-review classifier, the same shape as claude's auto mode. Measured: a safe command ran unprompted, footer Auto-review. Bypass still maps to --force; Ask each time stays bare.
- Route cursor raw hook events through one applyCursorRaw in both shells, so the subagent tracker runs and a child tool event cannot re-point the node's session. - Approval watch skips child tool calls by isCursorChildToolEvent. - PtyManager.sendText sends Enter as a separate write for SEPARATE_SUBMIT_AGENTS, so chat, phone and trigger sends submit in cursor. - Renderer retries a cursor model catalogue that came back empty. - installCursorHooks writes nothing on Windows until a cmd-safe command is measured. - docs/cursor-agent.md: drop stale local-only and single-read claims.
…ents, feed the activity line The claude raw branch recorded Cursor's (often child) chat id from claude.sh, so pty:destroy released the wrong session and the parent tail leaked. The subagent tracker gains release(nodeId), called by both shells' releaseNodeTails. applyCursorRaw records parent tool calls and turn ends for the phone's activity line; Shell reads like Bash.
readAgentSessionName resolves capabilityAgentId, so a custom agent on cursor reads the cursor store. performExitPhase asks submitsSeparately and waits EXIT_KEY_GAP_MS after Ctrl-U: measured in tmux, Ctrl-U+/quit in one burst exited 0/10, with the gap 10/10.
…ings search The --model sentence derives from MODEL_SWITCH_CAPABLE labels. Cursor gets the get-linked-context skill wording. The context menu probes cursor models only for cursor nodes. Settings search finds cursor.
sessionStart fires before any prompt on a plain launch but not on --resume <minted id>, which is how nodeterm launches every node, so it stays unsubscribed.
…base transfer - Cursor's colour moves from #6b7280 (OKLab 0.018 from Grok) to #d9468f, at least 0.12 from every swatch; a test pins agent swatch distance. - LINKED_CONTEXT_SKILL_AGENTS (claude, cursor) replaces the per-agent fork in noteLink, so claude-base and cursor-base custom agents match. - handoff.build resolves a custom agent's base before picking its reader, so a cursor-base node's Transfer works.
…Cursor in README - Shortcut markers this branch added now read note:, and its added em dashes are commas or colons. - CursorApprovalWatchDeps.delayMs had no caller; readsMs stays. - Comments say up to three pane reads (CURSOR_APPROVAL_READS_MS), the stop status note matches the captured fixture, and the docs record the live meter check, the blocked state and the relaunch exit clear. - README's agent list names Cursor.
Cursor's AskQuestion form fires no tool hook on show, answer or Esc (measured again: only afterAgentThought until the turn's stop). The approval watch now hangs its bounded pane reads on a quiet stretch of a working turn too (after beforeSubmitPrompt or a tool post, cancelled by preToolUse), and a box at the bottom of the pane emits waiting once, as Claude's AskUserQuestion does. The match is structural (footer, Question N of M, an option row), so the answered summary left in the transcript never matches. Fixture: live captures and hook payloads.
Reads the cursor-agent login (macOS Keychain via /usr/bin/security, else the CLI's auth.json) and asks the same endpoint the CLI's /usage uses, DashboardService/GetCurrentPeriodUsage on api2.cursor.sh. Shows total, Auto and API percentages with the billing-cycle reset. Visible by default with a Settings toggle. Not signed in or an expired token shows no row; a network or 5xx failure keeps the last good numbers of the same login.
The pill showed only a provider's worst limit, so Cursor read 100% (the API
bucket) while the plan total was far lower. Cursor now shows total, Auto and
API ("35% Cursor · 33% auto · 100% api"); every other provider keeps its single
worst-limit segment, text unchanged.
builtbyd3v
marked this pull request as ready for review
October 4, 2026 22:25
…t path Upstream's typed chat input is claude only, so a Cursor chat prompt still goes through sendText's paste, then a separate Enter. The per-pane write chain now serializes every sendText and sendEnvelope, which replaces the Cursor-only submit chain dropped in the merge. Pin both: cursor pastes and submits separately, claude takes the typed path, and an envelope waits for the Cursor Enter.
Cursor's hook installers already run only for a consented agent (the local registry and the remote installAgentHooks plan). Finish the rest of the consent model for it: - local skills go to ~/.cursor/skills, which cursor-agent loads before ~/.claude/skills, so canvas control does not depend on claude's consent - a decline deletes cursor.sh with the other agents' scripts - on an SSH host its skills ride ~/.agents/skills, and a removal strips only our entries from the shared ~/.cursor/hooks.json
…nto a dialog Resolve the pane's agent from create() or, after a restart, the workspace records when every placement agrees. Agent envelopes now paste, wait, then send a bare Enter inside their serialized write. The bare Enter is skipped while a Cursor approval, AskQuestion or plan prompt shows; an empty or failed capture still submits.
The byte cap let the newest blob through at any size. A blob over the whole budget is now skipped.
The 200 body was read with res.json() and no size limit. It is now read up to 256 KB; past that the read is cancelled and the row is unavailable.
…nvelope as busy Both Cursor splits now read the screen before pasting and write nothing while an approval, AskQuestion or plan prompt shows. A blind capture still sends. An envelope refused before the paste returns 'dialog', which delivery maps to targetBusy/blocked (retryable, queued) instead of targetGone. Once the paste is in, the envelope counts as written, so a withheld Enter reports stalled.
Conflicts resolved keeping both sides:
- shared/agents/config.ts: cursor joins CANVAS_CONTROL_CAPABLE; main's POSIX_TOOL_SHELL_ON_WINDOWS kept.
- core/agent-integrations.ts: main's Windows .cmd hook files kept; cursor stays POSIX-only (no Windows hook).
- core/agent-launch.ts: copilot's `--session-id=<uuid>` and cursor's `--resume <uuid>` mint grammars both kept.
- core/canvas-control-core.ts: both imports (MODEL_SWITCH_CAPABLE + shim-invocation).
- renderer/lib/verifyPanel.ts: usesLinkedContextSkill (cursor reads the skill) with main's hostPlatform-aware shim command.
Also de-flakes pty-manager.separate-submit.test.ts: the first rig's cold `import('./pty-manager')`
(1-3.5 s, more under a parallel run) ran inside that test's 5 s budget and timed it out. The module
now loads once in a file-level beforeAll with its own timeout; the test drops from seconds to ms.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main now registers 54 commands and this branch adds node.newAgent.cursor, so the 54-key pool ran out (expected 54 >= 55). Home and End pass normalizeBindingForCommand like the other navigation keys. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
eneskirca
enabled auto-merge (squash)
October 16, 2026 11:00
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
eneskirca
pushed a commit
that referenced
this pull request
Oct 16, 2026
#1128 landed while this PR was open. It is not a small addition: `cursor` joined almost every capability list at once — hooks, resume, subagents, context links, the usage meter, the chat view, rename, title read, canvas control, permission modes and model switching — so it gets its own paragraph rather than a clause. What it did NOT join is stated too: RECURRING_CAPABLE and BRANCH_CAPABLE are still Claude alone, so `/loop`, `/schedule`, `/cron` and Branch do not appear on a Cursor node. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Cursor
New
cursor-agentand works like the other agent nodes.--auto-review, Plan with--mode plan, Bypass with--force, Ask each time with no flag. Switch model lists Cursor's own models (cursor-agent models).cursor-agentalready stored and asks Cursor's own usage endpoint, the same one the CLI's/usageuses. On by default, with a toggle in Settings → Usage.cursor-agent.Improved
PtyManager.sendTextandsendEnvelopesend Enter as a separate write for agents that need it (only Cursor today). Cursor drops an Enter that arrives in the same write as a paste, so before this, chat, phone, trigger, canvaswriteand agentsend/replymessages were pasted but never submitted. Before the paste and again before the Enter, the pane is read: if Cursor's approval, question or plan box is on screen, nothing more is written, so a send never answers a dialog. An agent message held back this way is reported as busy (retryable), not as a gone session. After an app restart, a Cursor node that has not been opened yet is recognised from the saved workspace, so it gets the split too.--resume, so the chat composer used to refuse until the next turn.Good to know
cursor-agentis found, so nothing new runs for people who don't use it.cursor-agentended that turn withstatus: error("WritableIterable is closed" in the pane), so the node shows TURN FAILED. This happened in a plain terminal with only a hook logger, outside nodeterm. I haven't found the cause.docs/cursor-agent.md): on SSH nodes the chat view, meter and context link (the chat store is SQLite on the host), and/loopcards (the Cursor CLI has no loop feature). Windows gets no Cursor hooks until a cmd-safe hook command is measured.cursor-agent2026.10.01, live in the dev app, including Liquid Glass in dark and light (the glass trap probe found nothing). I haven't run it on Linux, Windows, the Server Edition or the phone. @eneskirca the phone app reads the status mirror andchat.pagefor Cursor the same way as the other agents; worth a look on the iOS side..nodeterm/project.json, so a hand-edited file can give a non-Cursor node the split Enter until that node is opened.main.