Sidekick - Cursor
Launches the Cursor CLI agent as a live call companion when Tuple transcription starts. It reads the transcript as the call happens, logs a running play-by-play, and chimes in when you say "Cursor".
Launches the Cursor CLI agent as a live companion on your Tuple call when transcription starts.
When call-transcription-started fires, this trigger opens your preferred terminal running cursor-agent inside the call's transcription directory. Cursor catches up on everything said so far, then follows the live transcript and acts as a sharp third pair on the call.
What it does
- Logs the call live. On every batch of new transcript, Cursor leaves a one-line
·play-by-play so you can follow along at a glance. - Chimes in when it matters. It escalates from a log line to a real interjection for a bug it can see, an ambiguous decision or action item, a correction, or a direct question.
- Answers when addressed. Say "Cursor, ..." (or type into the terminal) and it responds to that turn, then keeps following the call.
- Summarizes. It writes a checkpoint summary when recording stops and a final summary (decisions, action items, open threads) when the call ends.
Cursor follows the call with Tuple's bundled watcher (tuple-call-watcher.py), shipped with this trigger and run verbatim: a fixed, deterministic script rather than a watch loop the model re-authors each session. This is the same watcher the Codex, Claude, and OpenCode sidekicks use. Since the Cursor CLI has no event-driven wake (no equivalent of Claude Code's Monitor), it runs the watcher once with --catchup, then repeatedly in --exit-on-batch mode (each run blocks until the next batch, prints it, and exits). The Cursor agent runs shell commands synchronously and blocking, so a quiet stretch simply parks in the watcher until the next words land.
The prompt is passed as the initial message and no -p flag is used, so Cursor stays interactive afterward and you can type to it during the call.
Permissions
This trigger does not launch Cursor with a blanket auto-approve (--force / --yolo). Instead it pre-authorizes only the bundled watcher and leaves everything else to your own Cursor approval settings.
It does this by writing a project-scoped allowlist to <transcripts-root>/.cursor/cli.json (Cursor's working directory for the session):
{ "permissions": { "allow": ["Shell(*/tuple-call-watcher.py)"], "deny": [] } }
Cursor's Shell(<base>) rules match on the command's first token, so this permits ./<session-dir>/tuple-call-watcher.py ... (every session directory, including mid-call restarts) — and nothing else. The watcher loop runs unattended; any other command the agent attempts goes through your normal Cursor approval flow. The file is written only if you don't already have a .cursor/cli.json there, so a setup you manage yourself is never overwritten.
Two things to expect on first launch:
- Directory trust. Cursor asks once whether you trust the transcripts directory. Accept it to proceed (this is separate from command approval).
- Approval mode. Whether allowlisted commands run silently depends on your global Cursor approval mode (
~/.cursor/cli-config.json). With the default allowlist mode the watcher runs unattended; if your mode prompts for everything, approve the watcher once with "don't ask again."
MCP servers and plugins
The cursor-agent CLI loads MCP servers only from ~/.cursor/mcp.json (global) and the workspace's .cursor/mcp.json (project, wins on conflict). It does not load the MCP servers that Cursor IDE plugins install — those live in each plugin's own manifest and are an IDE-only feature.
This is per Cursor's own docs: the global path is ~/.cursor/mcp.json ("tools available everywhere") and the project path is .cursor/mcp.json ("project-specific tools") (MCP config), and the CLI "follows the same configuration precedence as the editor (project → global → nested)" (CLI MCP). Plugin-installed MCP servers are not among those documented sources — and in practice they don't show up in cursor-agent mcp list (also reported in the community: CLI does not detect MCP settings).
If your team ships approved/whitelisted MCPs as Cursor plugins (a common lockdown pattern), point the sidekick at those plugin directories and it will harvest their servers into the session's .cursor/mcp.json, which the CLI does read. Configure with these env vars (export in your shell profile; all off by default):
| Variable | Effect |
|---|---|
SIDEKICK_CURSOR_PLUGIN_DIRS |
Newline- or colon-separated Cursor plugin directories. For each, the trigger reads its mcpServers (from .cursor-plugin/plugin.json, plugin.json, or .mcp.json) and merges them into the session's .cursor/mcp.json. Your plugins stay the source of truth for what's approved; existing entries are preserved. |
SIDEKICK_CURSOR_APPROVE_MCPS=1 |
Adds --approve-mcps so those servers start without an approval prompt stalling the hands-free loop. |
SIDEKICK_CURSOR_EXTRA_ARGS |
Any other cursor-agent flags, shell-quoted (e.g. --model gpt-5.2). |
Example:
export SIDEKICK_CURSOR_PLUGIN_DIRS="$HOME/.cursor/plugins/cache/stash-plugins/stash/<key>"
export SIDEKICK_CURSOR_APPROVE_MCPS=1
(If you'd rather manage MCP servers directly, just maintain ~/.cursor/mcp.json — the CLI reads it everywhere, including the sidekick, with no env vars needed.)
Choosing your terminal
By default the trigger opens the first installed of Ghostty → iTerm → Alacritty → Terminal. To force one, set PREFERRED_TERM at the top of call-transcription-started (or in the environment):
PREFERRED_TERM="iterm" # ghostty | iterm | alacritty | terminal
The terminal runs launch-sidekick-cursor.command, whose #!/bin/zsh -li shebang sources your ~/.zprofile and ~/.zshrc, so cursor-agent resolves from the same PATH and environment you get in a normal terminal.
Prerequisites
- macOS
- Cursor CLI installed so
cursor-agentworks in a new terminal. Run it once first to sign in (and to clear any one-time workspace-trust prompt). python3(the bundled watcher needs it; install withxcode-select --install)- Tuple transcription enabled for the call
Installation
Drop this directory into your Tuple triggers folder:
~/.tuple/triggers/sidekick-cursor/
The trigger fires the next time call transcription starts.
How it works
When call-transcription-started fires, Tuple provides TUPLE_TRIGGER_CALL_ARTIFACTS_DIRECTORY, the directory containing the current call transcription artifacts. This trigger:
- Copies the fixed
tuple-call-watcher.pyand writessidekick-cursor-prompt.mdinto that directory. - Writes an executable
launch-sidekick-cursor.commandwrapper into that directory. - Opens it in your preferred terminal via
open(Ghostty → iTerm → Alacritty → Terminal; setPREFERRED_TERMto choose). No AppleScript, so it triggers no macOS accessibility prompt. - The wrapper starts a login-interactive zsh shell, changes to the transcripts root, and runs
cursor-agentwith the prompt as its initial message (no--force— see Permissions); Cursor runs the bundled watcher to catch up and follow the call.
A PID file (sidekick-cursor.pid) keeps a second transcription start from launching a duplicate sidekick for the same call.
For local script testing without opening a terminal, set SIDEKICK_CURSOR_DRY_RUN=1. It still ships the watcher and writes the launcher so you can inspect them.