mirror of
https://github.com/stablyai/orca.git
synced 2026-09-23 16:02:24 +00:00
* feat(cursor): first-class Cursor CLI agent status via ~/.cursor/hooks.json Give cursor-agent the same hook-driven status pipeline Claude/Codex/Gemini/ OpenCode already use so working/done/permission transitions show the proper sidebar spinner instead of falling back to title heuristics — cursor-agent only sets its OSC title to the literal string "Cursor Agent" during a turn, so title-based detection cannot see working→done transitions on its own. - New CursorHookService installs a managed shell script and registers it under ~/.cursor/hooks.json for beforeSubmitPrompt, preToolUse, postToolUse, postToolUseFailure, beforeShellExecution, beforeMCPExecution, stop, and afterAgentResponse (the subset that marks turn boundaries and surfaces in-flight tool context). - AgentHookServer gains a /hook/cursor route and a normalizeCursorEvent mapping the camelCase cursor events to working/done/waiting, with tool previews for preToolUse/shell/MCP and lastAssistantMessage from afterAgentResponse. stop with status != "completed" surfaces as interrupted (matches Claude's is_interrupt behavior). - cursorHookService joins the startup install loop alongside Claude/Codex/ Gemini, and the IPC status handler is exposed via preload. - 'cursor' added to WellKnownAgentType and to the renderer's WELL_KNOWN_LABELS so the dashboard prints "Cursor" rather than the raw 'cursor' id. Verified end-to-end against a real cursor-agent 2026.04.17-787b533 binary: a mock hook receiver pointed at by ~/.cursor/hooks.json observes beforeSubmitPrompt → stop for a live turn. Co-authored-by: Orca <help@stably.ai> * feat(cursor): wire hook events into sidebar spinner + unread pipeline The initial hook wiring landed behind AGENT_DASHBOARD_ENABLED, which is still false. That meant cursor-agent panes lit up no spinner and no unread indicator — cursor's native OSC title stays literally "Cursor Agent" across a turn, so title-based detection cannot transition. Plumb cursor's hook stream into the existing, shipped title-tracker pipeline (the one Claude/Codex/Pi drive working/idle/unread off) by synthesizing OSC title sequences in the main-process hook listener: - `working` → `\x1b]0;⠋ Cursor Agent\x07` (braille prefix → working) - `waiting` → `\x1b]0;Cursor - action required\x07\x07` - `done` → `\x1b]0;Cursor ready\x07\x07` The two trailing BELs on done/waiting are load-bearing: the unread badge keys off BEL (0x07 outside any OSC), and cursor-agent emits none on its own. The first BEL is consumed as the OSC terminator; the second fires the bell detector. Also treats the bare native "Cursor Agent" title as a no-op in `detectAgentStatusFromTitle` so cursor's own per-turn re-emissions cannot stomp our synthesized working state back to idle. `isClaudeAgent` excludes cursor-bearing braille titles so the Claude prompt-cache timer doesn't fire for cursor panes. Scope: the hook server + cursor install run unconditionally now, but Claude/Codex/Gemini installs stay gated behind AGENT_DASHBOARD_ENABLED, so only cursor events flow through the pipeline. No dashboard surface is turned on. Verified end-to-end in Electron (dev build): launched cursor-agent via the Cursor menu item, submitted three prompts (including a tool-use turn reading package.json). Observed tab title flip to "Cursor ready" on done, and the tab + worktree both transitioned to unread (unreadTerminalTabs[tabId]=true, worktree.isUnread=true). Parallel Claude Code pane untouched. Co-authored-by: Orca <help@stably.ai> * fix(cursor): animate spinner frames + filter bare native title so the spinner doesn't go solid mid-turn cursor-agent re-emits its bare "Cursor Agent" OSC title on every internal redraw, which was stomping the single synthesized "⠋ Cursor Agent" frame in runtimePaneTitlesByTabId within milliseconds and flipping the sidebar dot back to solid. Two-part fix: - Main: drive an 80ms Pi-style braille spinner from the cursor hook channel, keyed by paneKey, torn down on pty exit via a new registerPaneKeyTeardownListener hook in ipc/pty. - Renderer: drop bare "Cursor Agent" titles in pty-transport so cursor's native re-emissions cannot overwrite the synthesized working/idle titles. Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai>