Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions triggers/claude-call-sidekick/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules/
64 changes: 64 additions & 0 deletions triggers/claude-call-sidekick/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Claude Call Sidekick

Launch a [Claude Code](https://claude.ai/code) session when a Tuple call recording starts. Claude receives live transcription and call events via a Channels MCP server, acting as a silent sidekick that tracks the conversation and responds when asked.

Fires on the `call-recording-started` event. Opens a new terminal window running Claude Code with the transcription channel attached.

## What it does

- Feeds live Whisper transcription and call events (joins, leaves, screen sharing) into Claude's context
- Builds a system prompt with participant names, call ID, and your identity
- Captures screenshots of the Tuple window so Claude can see what's being shared on screen
- Claude stays silent unless you type a message or say "Claude" during the call

## Prerequisites

1. **Claude Code** — install via npm:

```sh
npm install -g @anthropic-ai/claude-code
```

2. **Node.js** v18 or later (for the MCP server)

3. **A Whisper model** configured in Tuple

4. **Screen Recording permission** (optional, for screenshots) — grant to your terminal app (Ghostty, Terminal.app, iTerm2, etc.) in **System Settings > Privacy & Security > Screen Recording**. You may need to restart your terminal after granting this.

Reach out to us at `support@tuple.app` if you want us to enable local call recording for you or your team.

## How it works

When recording starts, the trigger:

1. Creates a temporary MCP server config pointing to the bundled MCP server
2. Opens a `.command` file in your default terminal
3. Claude Code launches with the channel, receiving live transcription and events

The MCP server tails `transcriptions.jsonl` and `events.jsonl` from the call artifacts directory and pushes each new line to Claude as a channel notification.

### Screenshots

The MCP server exposes a `get_tuple_screenshot` tool that captures the Tuple app window using `swift` to find the window ID via CoreGraphics, then `screencapture -l` to capture it. Falls back to a full-screen capture if the window isn't found.

If Screen Recording permission isn't granted, screenshots will fail but Claude continues without visual context.

## Configuration

The trigger works out of the box with no configuration.

### Custom context

Create `~/.tuple/context.md` to give Claude additional context about your team, projects, or conventions. This file is appended to Claude's system prompt at the start of each call.

```markdown
## Team

- Alice (alice@example.com) — backend lead, owns the API layer
- Bob (bob@example.com) — frontend, working on the dashboard redesign

## Current projects

- Migrating from REST to GraphQL for the mobile app
- Q3 launch of the self-serve onboarding flow
```
Binary file added triggers/claude-call-sidekick/assets/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
72 changes: 72 additions & 0 deletions triggers/claude-call-sidekick/call-recording-started
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/usr/bin/env bash

TRIGGER_DIR="$(cd "$(dirname "$0")" && pwd)"

CALL_ID="$(basename "$(dirname "${TUPLE_TRIGGER_CALL_ARTIFACTS_DIRECTORY}")")"
SHORT_ID="${CALL_ID:0:8}"
SERVER_NAME="tuple-call-${SHORT_ID}"
WORK_DIR="/tmp/${SERVER_NAME}"

touch "${TUPLE_TRIGGER_CALL_ARTIFACTS_DIRECTORY}/transcriptions.jsonl" \
"${TUPLE_TRIGGER_CALL_ARTIFACTS_DIRECTORY}/events.jsonl"

# Install dependencies on first run
if [ ! -d "${TRIGGER_DIR}/node_modules" ]; then
npm install --prefix "${TRIGGER_DIR}" --silent 2>/dev/null
fi

# If the work dir already exists, a Claude Code session is already running
# for this call (recording was stopped and restarted). The channel server
# tails the files continuously, so it will pick up new content automatically.
if [ -d "${WORK_DIR}" ]; then
exit 0
fi

# First recording for this call — launch Claude Code
mkdir -p "${WORK_DIR}"

cat > "${WORK_DIR}/.mcp.json" <<MCPJSON
{
"mcpServers": {
"${SERVER_NAME}": {
"command": "npx",
"args": ["--yes", "tsx", "${TRIGGER_DIR}/mcp.ts"],
"env": {
"TUPLE_TRIGGER_CALL_ARTIFACTS_DIRECTORY": "${TUPLE_TRIGGER_CALL_ARTIFACTS_DIRECTORY}"
}
}
}
}
MCPJSON

# Build system prompt: base + optional company context
SYSTEM_PROMPT_FILE="${WORK_DIR}/system-prompt.md"
cp "${TRIGGER_DIR}/system-prompt.md" "${SYSTEM_PROMPT_FILE}"

# Derive Tuple config dir from artifacts path for context.md lookup
BUNDLE_ID=$(echo "${TUPLE_TRIGGER_CALL_ARTIFACTS_DIRECTORY}" | sed -n 's|.*/Caches/\([^/]*\)/.*|\1|p')
if [ "$BUNDLE_ID" = "app.tuple.staging" ]; then
CONTEXT_FILE="$HOME/.tuplestaging/context.md"
else
CONTEXT_FILE="$HOME/.tuple/context.md"
fi

if [ -f "$CONTEXT_FILE" ]; then
printf '\n---\n\n' >> "${SYSTEM_PROMPT_FILE}"
cat "$CONTEXT_FILE" >> "${SYSTEM_PROMPT_FILE}"
fi

# Write a .command file and open it (launches in the default terminal)
COMMAND_FILE="/tmp/${SERVER_NAME}.command"
cat > "${COMMAND_FILE}" <<SCRIPT
#!/bin/zsh -li
printf '\e]0;Tuple Call ${SHORT_ID}\a'
cd "${WORK_DIR}"
exec claude \
--model opus \
--effort low \
--dangerously-load-development-channels "server:${SERVER_NAME}" \
--append-system-prompt-file "${SYSTEM_PROMPT_FILE}"
SCRIPT
chmod +x "${COMMAND_FILE}"
open "${COMMAND_FILE}" &
6 changes: 6 additions & 0 deletions triggers/claude-call-sidekick/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "Claude Call Sidekick",
"description": "Launch a Claude Code session that listens to your call's live transcription and events via a Channels MCP server",
"platforms": ["macos"],
"language": "bash"
}
Loading
Loading