Skip to content

Repository files navigation

Exec

Voice-to-action system. Record a voice command on your phone or Mac, and a persistent Claude agent executes it with full Mac access.

Architecture

Phone (Expo) → InstantDB ← Agent (Bun + Claude Agent SDK) ← Desktop (Electron hotkey)

Three components:

  • Mobile app — Expo/React Native. Record voice, view task status + results
  • Agent process — Bun. Polls InstantDB for tasks, executes via Claude Agent SDK
  • Desktop hotkey — Electron. Cmd+Option+Space push-to-talk overlay

Prerequisites

  • Node.js 18+
  • Bun (agent process)
  • pnpm (mobile app)
  • EAS CLI (npm install -g eas-cli)
  • ffmpeg (desktop audio recording) — brew install ffmpeg
  • Xcode (iOS builds) / Android Studio (Android builds)

Environment Variables

Copy .env.example to .env and fill in:

EXPO_PUBLIC_INSTANT_APP_ID= # InstantDB app ID
EXPO_PUBLIC_OPENAI_API_KEY= # OpenAI API key (Whisper transcription + summaries)
INSTANT_APP_ADMIN_TOKEN= # InstantDB admin token (for agent)

The desktop app reads from desktop/.env:

INSTANT_APP_ID=
INSTANT_ADMIN_TOKEN=
OPENAI_API_KEY=

Development

Mobile App

pnpm install
pnpm start # Expo dev server
pnpm ios # Run on iOS simulator
pnpm android # Run on Android emulator

Agent Process

cd agent
bun install
bun run dev # Start with --watch (auto-restart on changes)

The agent polls InstantDB for pending tasks and executes them via the Claude Agent SDK with bypassPermissions. It processes tasks FIFO, one at a time.

Desktop Hotkey App

cd desktop
npm install
npm start # Launch Electron app

Press Cmd+Option+Space to record. Press again to stop, transcribe, and send to the agent. Runs as a menubar tray app.

Building & Deploying

EAS Build (Mobile)

Three build profiles in eas.json:

# Development build (includes dev tools, internal distribution)
eas build --profile development --platform ios
# Preview build (internal distribution, "preview" update channel)
eas build --profile preview --platform ios
# Production build (auto-increment version, "production" update channel)
eas build --profile production --platform ios

Replace ios with android for Android builds.

OTA Updates (Mobile)

Push over-the-air updates without rebuilding:

pnpm update:preview # Push to preview channel
pnpm update:production # Push to production channel

InstantDB Schema

After modifying instant.schema.ts or instant.perms.ts:

npx instant-cli push schema --app $EXPO_PUBLIC_INSTANT_APP_ID --token $INSTANT_APP_ADMIN_TOKEN --yes
npx instant-cli push perms --app $EXPO_PUBLIC_INSTANT_APP_ID --token $INSTANT_APP_ADMIN_TOKEN --yes

Auto-Start on Login (macOS)

launchd plists are included in the repo. Symlink them to ~/Library/LaunchAgents/ so macOS picks them up:

# Symlink the plists (one-time setup)
ln -s ~/ai/projects/exec/agent/com.exec.agent.plist ~/Library/LaunchAgents/
ln -s ~/ai/projects/exec/desktop/com.exec.desktop.plist ~/Library/LaunchAgents/
# Start them
launchctl bootstrap gui/$(id -u)~/Library/LaunchAgents/com.exec.agent.plist
launchctl bootstrap gui/$(id -u)~/Library/LaunchAgents/com.exec.desktop.plist

To stop:

launchctl bootout gui/$(id -u)/com.exec.agent
launchctl bootout gui/$(id -u)/com.exec.desktop

To restart:

launchctl kickstart -k gui/$(id -u)/com.exec.agent
launchctl kickstart -k gui/$(id -u)/com.exec.desktop

Logs: /tmp/exec-agent.stdout.log, /tmp/exec-desktop.stdout.log

Data Model

Two InstantDB entities:

tasks — voice commands and their results

FieldTypeNotes
inputstringTranscribed voice text
statusstringpending / running / done / failed / cancelled
resultstring?Agent's output (markdown)
sourcestringphone / mac
sessionIdstring?Claude session ID (for follow-ups)
liveOutputstring?Streaming output while running
cancelRequestedboolean?Cancel flag
createdAtnumberTimestamp (indexed, FIFO order)

messages — conversation thread per task

FieldTypeNotes
rolestringuser / assistant
contentstringMessage text
createdAtnumberTimestamp

Link: each message belongs to one task (cascade delete).

Project Structure

exec/
├── agent/ # Claude Agent SDK process
│ ├── index.ts # Main loop (FIFO queue, polling, execution)
│ ├── system-prompt.md # Agent system prompt
│ └── package.json
├── desktop/ # Electron hotkey app
│ ├── main.js # Main process (global shortcut, recording, transcription)
│ ├── overlay.html # Floating recording overlay
│ ├── preload.js # Context bridge
│ └── package.json
├── app/
│ ├── index.tsx # Home screen (recorder + task list + detail modal)
│ └── _layout.tsx # Root layout
├── components/
│ ├── RecordingOverlay.tsx # Full-screen recording UI
│ └── Waveform.tsx # Audio waveform visualization
├── lib/
│ ├── db.ts # InstantDB client init
│ ├── audio.ts # Audio file operations
│ └── transcription.ts # Groq Whisper API
├── hooks/
│ ├── useThemeColors.tsx # Dark/light theme
│ └── usePushNotifications.tsx
├── constants/Colors.ts # Design tokens
├── instant.schema.ts # InstantDB schema (2 entities)
├── instant.perms.ts # InstantDB permissions
├── eas.json # EAS build profiles
├── app.config.ts # Expo config
└── .env # Credentials

About

Voice → Done. Your AI executive that handles whatever you throw at it.

Topics

Resources

Stars

13 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages