Cursor support + multi-tool Coding Tools pane - #59
Open
dderuntz wants to merge 1 commit into
Open
Conversation
Bring the duck to Cursor (1.7+) alongside Claude Code, and add a unified surface for connecting multiple coding tools at once. The eval server is shared, so the duck can watch a Claude Code session and a Cursor agent simultaneously. Cursor integration (cursor/): - Adapter hooks translating Cursor's hook JSON to the existing /evaluate endpoint: beforeSubmitPrompt -> user scoring, afterAgentResponse -> response scoring (Cursor's `stop` carries no text, so we use the after-response event). - Permissions are a NON-BLOCKING NOTIFIER, not a gate. Cursor owns its own approval UI and ignores hook allow/ask anyway, so on-permission.sh fires a fire-and-forget /activity "pending" ping and returns instantly with no decision — Cursor's approve/deny is never influenced or blocked. The new CursorActivityMonitor pairs it with the afterShellExecution "resolved" ping and chirps once only if a command stays unresolved (Cursor parked on its approve prompt). Auto-run commands stay silent. MCP never chirps (no confirmed resolve event). - One-click install: widget writes ~/.cursor/hooks.json + copies scripts to a space-free path (~/.duck-duck-duck/cursor-hooks; Cursor runs commands via a shell, so a space in the path breaks them). Detects Cursor on launch and offers to connect out loud. Coding Tools preferences pane: - DuckIntegration protocol unifying Claude Code / Cursor / Gemini behind detect + connect/disconnect, with honest per-tool fidelity (Claude Code: voice permissions; Cursor: waiting-for-you alerts; Gemini: scores only). - Detection runs off the main thread (probes shell out) to avoid an AttributeGraph crash from blocking the SwiftUI render loop. Multi-tool attribution: - Hooks tag events with app + repo (folder basename, computed in-hook to dodge JSON slash-escaping). Server defaults app=claude-code, so existing payloads are unaffected. The duck names the program only for agent output, only on a real switch after a gap; user prompts are never attributed. Waiting chirps say program + repo. Design hardened via parallel adversarial review before implementation (resolved-before-pending race, denied-command leak, MainActor timer, MCP asymmetry, back-compat) — all folded in. The existing /permission voice gate and PermissionGate are untouched; Claude Code's flow is unchanged. Note: the "waiting" chirp's live firing is not yet confirmed on a genuinely parked approval (auto-run/allowlisted commands correctly stay silent); the non-blocking / non-influence guarantee is verified. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Collaborator
Author
|
@jftesser tagging you to explore this — Cursor support for the duck. The PR description has the architecture + how to trigger the "waiting" chirp. Shout with questions. |
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.
Brings the duck to Cursor (1.7+) alongside Claude Code, and adds a unified Coding Tools pane for connecting multiple tools at once. The eval server is shared, so the duck can watch a Claude Code session and a Cursor agent simultaneously.
Cursor integration (
cursor/)/evaluateendpoint —beforeSubmitPrompt→ user scoring,afterAgentResponse→ response scoring (Cursor'sstopcarries no text, so we use the after-response event).allow/ask(onlydenyis honored), soon-permission.shfires a fire-and-forget/activity"pending" ping and returns instantly with no decision — Cursor's approve/deny is never influenced or blocked.CursorActivityMonitorpairs it with theafterShellExecution"resolved" ping and chirps once only if a command stays unresolved (Cursor parked on its approve prompt). Auto-run commands stay silent. MCP never chirps (no confirmed resolve event).~/.cursor/hooks.json+ copies scripts to a space-free path (~/.duck-duck-duck/cursor-hooks; Cursor runs commands through a shell, so a space in the path breaks them with exit 127). Detects Cursor on launch and offers to connect out loud.Coding Tools preferences pane
DuckIntegrationprotocol unifies Claude Code / Cursor / Gemini behind detect + connect/disconnect, with honest per-tool fidelity (Claude Code: voice permissions; Cursor: waiting-for-you alerts; Gemini: scores only).Multi-tool attribution
app+repo(folder basename, computed in-hook to dodge JSON slash-escaping). Server defaultsapp=claude-code, so existing payloads are unaffected. The duck names the program only for agent output, only on a real switch after a gap; user prompts are never attributed. Waiting chirps say program + repo.Hardening
Design was hardened via a parallel adversarial review before implementation — resolved-before-pending race (MiniServer spawns unordered Tasks), denied-command leak, MainActor timer (a bare
Timeron the server queue never fires), MCP asymmetry, back-compat — all folded in and unit-simulated. The existing/permissionvoice gate andPermissionGateare untouched; Claude Code's flow is unchanged.Status / how to explore
CursorActivityMonitor.parkedThreshold.To try:
make run→ Coding Tools → Connect, restart Cursor. After pulling new changes, Disconnect → Connect to refresh the installed scripts.🤖 Generated with Claude Code