Skip to content

Cursor support + multi-tool Coding Tools pane - #59

Open
dderuntz wants to merge 1 commit into
mainfrom
feature/cursor-support
Open

Cursor support + multi-tool Coding Tools pane#59
dderuntz wants to merge 1 commit into
mainfrom
feature/cursor-support

Conversation

@dderuntz

Copy link
Copy Markdown
Collaborator

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/)

  • Scoring: adapter hooks translate 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 = non-blocking notifier, not a gate. Cursor owns its own approval UI and ignores hook allow/ask (only deny is honored), 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. 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 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

  • DuckIntegration protocol 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).
  • Detection runs off the main thread (it shells 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.

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 Timer on the server queue never fires), MCP asymmetry, back-compat — all folded in and unit-simulated. The existing /permission voice gate and PermissionGate are untouched; Claude Code's flow is unchanged.

Status / how to explore

  • Verified: Cursor wiring fires correctly; scoring works in chat and agent; non-blocking + non-influence guarantee (permission prompts are never blocked or decided by the duck); the install/refresh flow; back-compat.
  • Not yet verified: the live "waiting" chirp on a genuinely parked approval. Auto-run/allowlisted commands correctly stay silent — to see the chirp, turn off Cursor's auto-run/allowlist (or run a command it will actually stop and ask about) and wait ~2.5s. Threshold lives in CursorActivityMonitor.parkedThreshold.

To try: make runCoding Tools → Connect, restart Cursor. After pulling new changes, Disconnect → Connect to refresh the installed scripts.

🤖 Generated with Claude Code

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>
@dderuntz
dderuntz requested a review from jftesser June 21, 2026 17:14
@dderuntz

Copy link
Copy Markdown
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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant