The Context Switch That Kills Your Flow

You're deep in a debugging session. You find it — the token expiry is set to 5 minutes but the password reset email takes 10 minutes to arrive. Users can't log in. You need to file a GitHub issue before you context-switch away and lose the details.

So you open GitHub. Navigate to the repo. Click "New issue." Type the title. Write the body. Add labels. Click "Submit new issue."

By the time you're done, two minutes have passed. You've lost the mental model of the auth flow you were tracing. You switch back to the code. Where were you again?

What if you could just speak?

⌘5

"Users can't log in after password reset — the token expiry is set to 5 minutes but the email takes 10 minutes to arrive."

Release. SpeechButton transcribes your words, transforms them into a structured GitHub issue, and sends it through the REST API. A new issue appears in your repository:

#142: Users can't log in after password reset — token expiry too short

Password reset tokens expire after 5 minutes, but the reset email can take up to 10 minutes to arrive. By the time users click the link, the token is already invalid and they're unable to log in.

Labels: bug, auth

Three seconds. No browser. No clicking. No context switch. You're still in your editor, still tracing the auth flow.

How It Works

Your voice ──▶ SpeechButton STT ──▶ Gemma 4 Local AI ──▶ GitHub REST API
  (7ms)         (Parakeet V3,        (structures as         (creates issue)
                 100% offline)         title + body +
                                       labels, local)

Three components:

  1. SpeechButton captures and transcribes your voice locally on Apple Neural Engine
  2. Local AI (Gemma 4) reads your prompt file and extracts a structured issue (title, body, labels) from your transcription — entirely on your Mac
  3. A Python script in your integrations/ folder sends the JSON to GitHub's REST API

The result: a well-formatted GitHub issue from your spoken description, in under 4 seconds total. Everything is local except the final GitHub API call — no Anthropic API key needed.

Setup

Four steps. Under five minutes.

Step 1: Get a GitHub token

  1. Go to github.com/settings/tokens → "Fine-grained tokens" → "Generate new token"
  2. Set repository access to the repos you want to create issues in
  3. Under Permissions → Repository permissions, set Issues to Read & Write
  4. Copy the token (starts with github_pat_)

Step 2: Create the prompt file

Save this as ~/.config/speechbutton/prompts/github_issue.md. It tells the local AI how to structure your speech into a JSON payload for GitHub.

markdown — ~/.config/speechbutton/prompts/github_issue.md
Convert speech into a GitHub issue JSON. Extract:
- title: concise issue title (under 80 chars)
- body: detailed markdown body with steps to reproduce
- labels: array of label strings (bug, enhancement, etc.)

Output ONLY valid JSON:
{"title": "...", "body": "...", "labels": ["bug"]}

Step 3: SpeechButton config.toml

Add a hotkey for GitHub Issues. RightCommand creates an issue; the default left Command still pastes at cursor.

toml — ~/.config/speechbutton/config.toml
# ~/.config/speechbutton/config.toml

[global]
model = "parakeet-tdt-0.6b-v3-int8"
language = "en"
auto_punctuation = true

[audio]
vad_enabled = true
vad_silence_threshold = 1.0

# GitHub — create issue from voice
[[hotkey]]
key = "RightCommand"
channel = "5"
name = "github"
transform = "prompts/github_issue.md"
exec = "GITHUB_TOKEN=ghp_xxx GITHUB_REPO=owner/repo integrations/send_github.py"

Replace ghp_xxx with your token and owner/repo with your repository (e.g. acme/backend).

Step 4: Integration script — create the issue via GitHub API

Takes the structured JSON from stdin and creates the issue via GitHub's REST API. No external dependencies — uses only Python's standard library.

Option A — Recommended Local AI · Free · Offline · Private

Uses Gemma 4 running locally on your Mac. No API key required. No data leaves your machine except the final GitHub issue.

transform = "prompts/github_issue.md"
Option B — Alternative Claude API · Requires API key · Costs money

Uses Claude API for potentially more accurate structuring. Requires an ANTHROPIC_API_KEY and incurs per-request costs.

transform = "transforms/transform_claude.py prompts/github_issue.md"

The rest of this guide uses Option A (local AI). If you choose Option B, set ANTHROPIC_API_KEY in your environment and swap the transform line in config.toml. The integration script and prompt file are identical for both options.

python — ~/.config/speechbutton/integrations/send_github.py
#!/usr/bin/env python3
"""Create a GitHub issue from stdin JSON."""
import json, os, sys, urllib.request

def main():
    text = sys.stdin.read().strip()
    if not text: sys.exit(0)

    # Strip markdown code fences
    if text.startswith("```"):
        lines = text.split("\n")
        text = "\n".join(lines[1:-1] if lines[-1].startswith("```") else lines[1:])

    try:
        data = json.loads(text)
    except json.JSONDecodeError:
        data = {"title": text[:80], "body": text, "labels": []}

    token = os.environ.get("GITHUB_TOKEN")
    repo = os.environ.get("GITHUB_REPO")
    if not token or not repo:
        print("GITHUB_TOKEN and GITHUB_REPO required", file=sys.stderr)
        sys.exit(1)

    req = urllib.request.Request(
        f"https://api.github.com/repos/{repo}/issues",
        json.dumps({"title": data.get("title", "Untitled"),
                     "body": data.get("body", ""),
                     "labels": data.get("labels", [])}).encode(),
        {"Authorization": f"Bearer {token}",
         "Accept": "application/vnd.github+json",
         "Content-Type": "application/json"})
    result = json.loads(urllib.request.urlopen(req, timeout=15).read())
    print(f"Created #{result['number']}: {result.get('title','')[:50]}")

if __name__ == "__main__":
    main()
bash — make executable
chmod +x ~/.config/speechbutton/integrations/send_github.py

Done. Hold RightCommand, describe a bug, release. GitHub issue created.

Real Workflows

Bug report

You're staring at the auth logs and the root cause is obvious. File it before the context evaporates:

⌘5

"Users can't log in after password reset — the token expiry is set to 5 minutes but the email takes 10 minutes to arrive."

A labeled bug issue with the full context, filed while the details are still in your head.

Feature request

You're in Discord, someone asks about dark mode for the third time this week. Log it without switching windows:

⌘5

"Add dark mode to the settings page — users have been asking for it in the Discord."

Enhancement issue filed, labeled, and in the backlog. You never left Discord.

Technical task

You're planning the mobile app and realize the auth module needs refactoring first:

⌘5

"Migrate the auth module from JWT to session-based auth — needed for the mobile app."

A clean task issue with the right label and context. Ready for the next sprint.

Why Voice Beats Typing for Issue Creation

GitHub issues created by typing tend to be either too terse ("fix auth") or take too long to write well. Voice hits the sweet spot:

You speak naturally

"Users can't log in after password reset — the token expiry is set to 5 minutes but the email takes 10 minutes to arrive."

The transform structures it

Users can't log in after password reset — token expiry too short

Password reset tokens expire after 5 minutes, but the reset email can take up to 10 minutes to arrive.

Labels: bug, auth

The result is a better issue than you'd type manually, created in a fraction of the time. The AI transform handles the formatting you'd skip when rushing and the structure you'd over-think when not.

And because SpeechButton captures in 7ms, you can rapid-fire multiple issues in sequence. Hold, speak, release. Hold, speak, release. Three GitHub issues filed before you'd finish typing the first one.

Privacy

Here's exactly what stays on your Mac and what goes to the cloud:

Component Where it runs Data sent externally
Voice capture Your Mac Nothing
Speech-to-text (Parakeet V3) Apple Neural Engine Nothing
AI transform (default) Your Mac (Gemma 4, local) Nothing
GitHub API call Your Mac Structured issue → GitHub's servers

With the default local AI transform, everything stays on your Mac except the final GitHub API call: voice → local STT (Parakeet V3, Apple Neural Engine) → local AI transform (Gemma 4, on your Mac) → GitHub API. No audio leaves your machine. No transcription leaves your machine. Only the structured issue data reaches GitHub's servers — which is where it needs to go anyway. No Anthropic API key required. If you prefer higher accuracy at the cost of privacy, you can optionally switch to the Claude API transform (Option B) — in that case, transcribed text is sent to Anthropic's servers for structuring.

Prerequisites

  • macOS 15 or later
  • Apple Silicon (M1 or newer)
  • GitHub account with a repository to file issues in
  • A fine-grained GitHub token with Issues: Read & Write permission
  • Python 3 (pre-installed on macOS)

Get Started

  1. 1 Download SpeechButton — free 15 minutes/day, no account needed
  2. 2 Get your GitHub token — github.com/settings/tokens → Fine-grained → Issues: Read & Write
  3. 3 Copy the config, prompt file, and integration script from this article into ~/.config/speechbutton/
  4. 4 Make the integration script executable: chmod +x ~/.config/speechbutton/integrations/send_github.py
  5. 5 Hold RightCommand, describe an issue, release. Your first voice-created GitHub issue in under a minute.

Start creating issues by voice today

Free 15 min/day · No account needed · macOS 15+ · Apple Silicon

 Download for macOS — Free

Pro ($7.99/mo) removes the daily limit. Requires macOS 15+ and Apple Silicon.