feat(agents): add Cursor Agent runtime support#545
Conversation
Greptile SummaryThis PR registers Cursor Agent as a first-class runtime in Kanban, wiring detection, auto-selection, session launch, onboarding UI, and home-sidebar prompt injection across the full stack.
Confidence Score: 3/5The binary detection and UI wiring are solid, but the cursor adapter omits the hook configuration step that every other adapter performs, leaving task state transitions unimplemented for Cursor sessions. The cursor adapter propagates hook env vars but never writes a hook configuration file or passes hook flags to Cursor — unlike claude, codex, gemini, droid, kiro, and cline adapters which all register agent-side callbacks. Without it, tasks will not auto-transition from in_progress to review states. The Linear MCP guidance also hardcodes the agent alias in a runnable command, which breaks on installs where only cursor-agent is available. src/terminal/agent-session-adapters.ts (cursorAdapter hook setup) and src/prompts/append-system-prompt.ts (Linear MCP binary reference) need attention before merging.
|
| Filename | Overview |
|---|---|
| src/terminal/agent-session-adapters.ts | Adds cursorAdapter with plan/force/continue flag handling and hook env propagation, but unlike every other adapter, it writes no hook configuration file — meaning Cursor Agent has no registration to call Kanban webhooks on task completion. |
| src/prompts/append-system-prompt.ts | Adds Cursor to the home-agent prompt list and provides Linear MCP guidance; the guidance hardcodes the agent alias binary rather than the canonical cursor-agent, which will cause a command-not-found failure on systems where only cursor-agent is installed. |
| src/core/agent-catalog.ts | Adds Cursor catalog entry with cursor-agent as canonical binary and agent as alias; introduces getRuntimeAgentBinaryCandidates helper. The agent alias is generic but the design tradeoff is acknowledged in the PR. |
| src/core/api-contract.ts | Adds cursor to runtimeAgentIdSchema enum — straightforward schema extension with no issues. |
| src/terminal/agent-registry.ts | Updates detectInstalledCommands and resolveAgentCommand to use getRuntimeAgentBinaryCandidates, correctly preferring the first detected binary. Logic is clean and well-tested. |
| src/config/runtime-config.ts | Adds cursor to auto-selection priority and normalizeAgentId; switches from single-binary to multi-candidate detection. Correct and consistent with catalog additions. |
| web-ui/src/components/runtime-settings-dialog.tsx | Inserts cursor into SETTINGS_AGENT_ORDER list — trivial and correct. |
| web-ui/src/components/task-start-agent-onboarding-carousel.tsx | Adds cursor to onboarding list with install description and install link label — straightforward UI extension. |
Sequence Diagram
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant K as Kanban Runtime
participant R as agent-registry
participant C as agent-catalog
participant A as cursorAdapter
participant CUR as cursor-agent / agent
K->>R: detectInstalledCommands()
R->>C: RUNTIME_AGENT_CATALOG.flatMap(binary + aliases)
note over R: checks cursor-agent, then agent
R-->>K: detectedCommands[]
K->>R: pickBestInstalledAgentIdFromDetected()
note over R: cursor before codex in priority list
R-->>K: cursor
K->>R: resolveAgentCommand(config)
R->>C: getRuntimeAgentBinaryCandidates(cursor)
C-->>R: [cursor-agent, agent]
R-->>K: "ResolvedAgentCommand binary=cursor-agent"
K->>A: prepareAgentLaunch(input)
A->>A: removeCursorPlanModeConflicts() if plan mode
A->>A: push --plan / --force / --continue flags
A->>A: resolveHookContext set KANBAN_HOOK env vars
A->>A: mergeCursorPromptWithHomeSystemPrompt()
A-->>K: PreparedAgentLaunch
K->>CUR: spawn binary with args + env
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant K as Kanban Runtime
participant R as agent-registry
participant C as agent-catalog
participant A as cursorAdapter
participant CUR as cursor-agent / agent
K->>R: detectInstalledCommands()
R->>C: RUNTIME_AGENT_CATALOG.flatMap(binary + aliases)
note over R: checks cursor-agent, then agent
R-->>K: detectedCommands[]
K->>R: pickBestInstalledAgentIdFromDetected()
note over R: cursor before codex in priority list
R-->>K: cursor
K->>R: resolveAgentCommand(config)
R->>C: getRuntimeAgentBinaryCandidates(cursor)
C-->>R: [cursor-agent, agent]
R-->>K: "ResolvedAgentCommand binary=cursor-agent"
K->>A: prepareAgentLaunch(input)
A->>A: removeCursorPlanModeConflicts() if plan mode
A->>A: push --plan / --force / --continue flags
A->>A: resolveHookContext set KANBAN_HOOK env vars
A->>A: mergeCursorPromptWithHomeSystemPrompt()
A-->>K: PreparedAgentLaunch
K->>CUR: spawn binary with args + env
Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 3
src/terminal/agent-session-adapters.ts:767-776
**Missing hook configuration file — task state transitions won't fire**
Every other adapter that supports hooks (claude, codex, gemini, opencode, droid, kiro, cline) writes a hook configuration file and passes it to the CLI, so the agent actually knows to call back into Kanban on `Stop`, `PreToolUse`, etc. The cursor adapter only sets `KANBAN_HOOK_TASK_ID`/`KANBAN_HOOK_WORKSPACE_ID` env vars, but never registers any hook command with Cursor Agent. Without a hook config, Cursor has no instruction to call the Kanban webhook, so tasks will stay stuck in `in_progress` indefinitely rather than transitioning to `awaiting_review`.
### Issue 2 of 3
src/prompts/append-system-prompt.ts:62-63
**Linear MCP guidance hardcodes the `agent` alias, which may not exist**
The detection layer preferentially uses `cursor-agent` as the canonical binary; `agent` is only a fallback alias. If a user's install exposes `cursor-agent` but not `agent` as a standalone command, Cursor Agent will try to run `agent mcp login linear` and get a "command not found" error. The guidance should mention both possible binaries so the instruction stays valid regardless of which binary was resolved.
```suggestion
case "cursor":
return "- If Linear MCP is not available in the current agent (Cursor Agent), suggest adding `linear` in `.cursor/mcp.json` or `~/.cursor/mcp.json`, then run: `cursor-agent mcp login linear` (or `agent mcp login linear` if `cursor-agent` is not available)";
```
### Issue 3 of 3
src/core/agent-catalog.ts:33-35
**Generic `agent` alias risks false-positive Cursor detection**
`agent` is an extremely common binary name used by unrelated tools (e.g., HashiCorp Vault Agent, New Relic Agent, AWS SSM Agent). Because `cursor` now sits ahead of `codex` in `AUTO_SELECT_AGENT_PRIORITY`, any system with a non-Cursor `agent` binary on PATH will have Cursor auto-selected over Codex, and `resolveAgentCommand` will return that unrelated binary as the Cursor binary. The PR notes this tradeoff, but it may be worth documenting a follow-up to narrow detection or at least adding a comment explaining the known risk.
Reviews (1): Last reviewed commit: "feat(agents): add Cursor Agent runtime s..." | Re-trigger Greptile
| const hooks = resolveHookContext(input); | ||
| if (hooks) { | ||
| Object.assign( | ||
| env, | ||
| createHookRuntimeEnv({ | ||
| taskId: hooks.taskId, | ||
| workspaceId: hooks.workspaceId, | ||
| }), | ||
| ); | ||
| } |
There was a problem hiding this comment.
Missing hook configuration file — task state transitions won't fire
Every other adapter that supports hooks (claude, codex, gemini, opencode, droid, kiro, cline) writes a hook configuration file and passes it to the CLI, so the agent actually knows to call back into Kanban on Stop, PreToolUse, etc. The cursor adapter only sets KANBAN_HOOK_TASK_ID/KANBAN_HOOK_WORKSPACE_ID env vars, but never registers any hook command with Cursor Agent. Without a hook config, Cursor has no instruction to call the Kanban webhook, so tasks will stay stuck in in_progress indefinitely rather than transitioning to awaiting_review.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/terminal/agent-session-adapters.ts
Line: 767-776
Comment:
**Missing hook configuration file — task state transitions won't fire**
Every other adapter that supports hooks (claude, codex, gemini, opencode, droid, kiro, cline) writes a hook configuration file and passes it to the CLI, so the agent actually knows to call back into Kanban on `Stop`, `PreToolUse`, etc. The cursor adapter only sets `KANBAN_HOOK_TASK_ID`/`KANBAN_HOOK_WORKSPACE_ID` env vars, but never registers any hook command with Cursor Agent. Without a hook config, Cursor has no instruction to call the Kanban webhook, so tasks will stay stuck in `in_progress` indefinitely rather than transitioning to `awaiting_review`.
How can I resolve this? If you propose a fix, please make it concise.| case "cursor": | ||
| return "- If Linear MCP is not available in the current agent (Cursor Agent), suggest adding `linear` in `.cursor/mcp.json` or `~/.cursor/mcp.json`, then run: `agent mcp login linear`"; |
There was a problem hiding this comment.
Linear MCP guidance hardcodes the
agent alias, which may not exist
The detection layer preferentially uses cursor-agent as the canonical binary; agent is only a fallback alias. If a user's install exposes cursor-agent but not agent as a standalone command, Cursor Agent will try to run agent mcp login linear and get a "command not found" error. The guidance should mention both possible binaries so the instruction stays valid regardless of which binary was resolved.
| case "cursor": | |
| return "- If Linear MCP is not available in the current agent (Cursor Agent), suggest adding `linear` in `.cursor/mcp.json` or `~/.cursor/mcp.json`, then run: `agent mcp login linear`"; | |
| case "cursor": | |
| return "- If Linear MCP is not available in the current agent (Cursor Agent), suggest adding `linear` in `.cursor/mcp.json` or `~/.cursor/mcp.json`, then run: `cursor-agent mcp login linear` (or `agent mcp login linear` if `cursor-agent` is not available)"; |
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/prompts/append-system-prompt.ts
Line: 62-63
Comment:
**Linear MCP guidance hardcodes the `agent` alias, which may not exist**
The detection layer preferentially uses `cursor-agent` as the canonical binary; `agent` is only a fallback alias. If a user's install exposes `cursor-agent` but not `agent` as a standalone command, Cursor Agent will try to run `agent mcp login linear` and get a "command not found" error. The guidance should mention both possible binaries so the instruction stays valid regardless of which binary was resolved.
```suggestion
case "cursor":
return "- If Linear MCP is not available in the current agent (Cursor Agent), suggest adding `linear` in `.cursor/mcp.json` or `~/.cursor/mcp.json`, then run: `cursor-agent mcp login linear` (or `agent mcp login linear` if `cursor-agent` is not available)";
```
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| binary: "cursor-agent", | ||
| binaryAliases: ["agent"], | ||
| baseArgs: [], |
There was a problem hiding this comment.
Generic
agent alias risks false-positive Cursor detection
agent is an extremely common binary name used by unrelated tools (e.g., HashiCorp Vault Agent, New Relic Agent, AWS SSM Agent). Because cursor now sits ahead of codex in AUTO_SELECT_AGENT_PRIORITY, any system with a non-Cursor agent binary on PATH will have Cursor auto-selected over Codex, and resolveAgentCommand will return that unrelated binary as the Cursor binary. The PR notes this tradeoff, but it may be worth documenting a follow-up to narrow detection or at least adding a comment explaining the known risk.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/core/agent-catalog.ts
Line: 33-35
Comment:
**Generic `agent` alias risks false-positive Cursor detection**
`agent` is an extremely common binary name used by unrelated tools (e.g., HashiCorp Vault Agent, New Relic Agent, AWS SSM Agent). Because `cursor` now sits ahead of `codex` in `AUTO_SELECT_AGENT_PRIORITY`, any system with a non-Cursor `agent` binary on PATH will have Cursor auto-selected over Codex, and `resolveAgentCommand` will return that unrelated binary as the Cursor binary. The PR notes this tradeoff, but it may be worth documenting a follow-up to narrow detection or at least adding a comment explaining the known risk.
How can I resolve this? If you propose a fix, please make it concise.|
Addressed the Greptile review in commit
Validation after the fixes:
|
|
For reviewers comparing this with #253, the intended decision frame is:
The main design tradeoff left for maintainers is whether project-level |
Summary
cursor-agentas the canonical binary andagentas a fallback launch alias--force,--continue,--plan, project-level.cursor/hooks.jsontask lifecycle hooks, and cleanup that restores pre-existing hook configagentbinary; auto-selection requires canonicalcursor-agentor explicitcursorselectionFixes #544
Supersedes #253
Test plan
npm test -- test/runtime/config/runtime-config.test.ts test/runtime/terminal/agent-registry.test.ts test/runtime/terminal/agent-session-adapters.test.ts test/runtime/append-system-prompt.test.tsnpm --prefix web-ui run test -- src/components/runtime-settings-dialog.test.tsx src/hooks/use-startup-onboarding.test.tsx src/runtime/use-runtime-config.test.tsx src/runtime/use-runtime-project-config.test.tsxnpm run typechecknpm --prefix web-ui run typechecknpx @biomejs/biome check src/config/runtime-config.ts src/core/agent-catalog.ts src/core/api-contract.ts src/prompts/append-system-prompt.ts src/terminal/agent-registry.ts src/terminal/agent-session-adapters.ts test/runtime/append-system-prompt.test.ts test/runtime/config/runtime-config.test.ts test/runtime/terminal/agent-registry.test.ts test/runtime/terminal/agent-session-adapters.test.ts web-ui/src/components/runtime-settings-dialog.tsx web-ui/src/components/task-start-agent-onboarding-carousel.tsxnpm run typecheckandnpm run test:fastwith inherited Kanban task runtime env clearedNotes
Cursor's current CLI surface can expose both
cursor-agentandagent. This PR usescursor-agentas the clearer canonical command while acceptingagentonly as a fallback when Cursor is already selected.Compared with #253, this PR is based on current
main, keeps alias handling centralized in the runtime catalog, avoids genericagentauto-selection false positives, and wires Cursor task state transitions through.cursor/hooks.json.