{
  "schema_version": "1",
  "name": "FunButton",
  "tagline": "Local-first push-to-talk dictation for macOS.",
  "homepage": "https://funbutton.ai",
  "repo": "https://github.com/todddickerson/funbutton",
  "license": "GPL-3.0-or-later",
  "latest_version": "0.1.8",
  "platform": {
    "os": "macOS",
    "min_macos": "12.0",
    "arch": "arm64",
    "note": "Apple Silicon only. There is no Intel/x86_64 build."
  },
  "signed": false,
  "signing_note": "The alpha is unsigned (no Apple Developer ID yet), so left untouched macOS mislabels it as \"damaged.\" The brew cask and the curl installer strip the com.apple.quarantine flag from FunButton.app only. Never disable Gatekeeper globally (no `spctl --master-disable`).",
  "download": {
    "url": "https://funbutton.ai/download",
    "asset_name": "FunButton_0.1.8_aarch64.dmg",
    "asset_url": "https://github.com/todddickerson/funbutton/releases/download/v0.1.8/FunButton_0.1.8_aarch64.dmg",
    "sha256": "c186f57dec651c9a43dd104a287985bf6a8dae9beed1b9794ae940fc24b1285a",
    "size_bytes": 17050262
  },
  "install_methods": [
    {
      "id": "brew",
      "preferred": true,
      "title": "Homebrew cask",
      "commands": [
        "brew install --cask todddickerson/funbutton/funbutton"
      ],
      "upgrade": "brew upgrade --cask funbutton",
      "clears_quarantine": true,
      "success_contains": "was successfully installed",
      "note": "The cask's postflight strips com.apple.quarantine from FunButton.app only. brew does NOT strip quarantine by default."
    },
    {
      "id": "curl",
      "preferred": false,
      "title": "curl one-liner",
      "commands": [
        "curl -fsSL https://funbutton.ai/install.sh | bash"
      ],
      "clears_quarantine": true,
      "success_contains": "FunButton is installed",
      "readable_source": "https://funbutton.ai/install.sh",
      "note": "No sudo, no global Gatekeeper change. Readable at the URL before you run it."
    },
    {
      "id": "manual",
      "preferred": false,
      "title": "Manual .dmg (last resort)",
      "commands": [
        "curl -fL -o /tmp/FunButton.dmg https://funbutton.ai/download",
        "mkdir -p /tmp/fb-mnt",
        "hdiutil attach /tmp/FunButton.dmg -nobrowse -noautoopen -mountpoint /tmp/fb-mnt",
        "rm -rf /Applications/FunButton.app",
        "ditto /tmp/fb-mnt/FunButton.app /Applications/FunButton.app",
        "hdiutil detach /tmp/fb-mnt",
        "xattr -dr com.apple.quarantine /Applications/FunButton.app"
      ],
      "clears_quarantine": false,
      "note": "The only path that does NOT clear quarantine for you — the final xattr line is required. Never run `spctl --master-disable`."
    }
  ],
  "required_permissions": [
    {
      "name": "Microphone",
      "why": "capture your voice while you hold the button",
      "settings_pane": "System Settings > Privacy & Security > Microphone"
    },
    {
      "name": "Accessibility",
      "why": "paste the cleaned text at your cursor",
      "settings_pane": "System Settings > Privacy & Security > Accessibility"
    },
    {
      "name": "Input Monitoring",
      "why": "detect your push-to-talk key press",
      "settings_pane": "System Settings > Privacy & Security > Input Monitoring"
    }
  ],
  "permissions_grantable_by_agent": false,
  "permissions_note": "These are macOS TCC permissions. macOS reserves them for a human at an unlocked screen — no agent, script, tccutil, or MDM profile can grant them silently. Install and verify, then hand the three toggles off to the user.",
  "hotkey": {
    "user_selectable": true,
    "options": [
      "Fn",
      "Right Control",
      "Left Control",
      "Right Command",
      "Left Command",
      "Right Option",
      "Caps Lock"
    ],
    "note": "The app detects the keyboard. On extended Apple keyboards the bottom-left key is Control, not Fn."
  },
  "models": {
    "bundled": false,
    "when": "first run",
    "download_size_human": "~1.1 GB",
    "download_size_bytes": 1202206944,
    "location": "~/Library/Application Support/ai.funbutton.desktop/models/",
    "resumable": true,
    "verified": "sha-256",
    "files": [
      "qwen2.5-1.5b-instruct-q4_k_m.gguf",
      "whisper-base.en-Q8_0.gguf"
    ],
    "note": "Not bundled in the app. Surface this before it happens so a first-run network pull isn't a surprise. After it downloads, transcription and cleanup run fully offline."
  },
  "verify": [
    {
      "id": "app_present",
      "command": "test -d /Applications/FunButton.app && echo OK",
      "expect": "OK"
    },
    {
      "id": "no_quarantine",
      "command": "xattr /Applications/FunButton.app",
      "expect_absent": "com.apple.quarantine",
      "note": "Output must NOT contain com.apple.quarantine."
    },
    {
      "id": "version",
      "command": "defaults read /Applications/FunButton.app/Contents/Info CFBundleShortVersionString",
      "expect": "0.1.8",
      "note": "Should equal latest_version."
    },
    {
      "id": "launch",
      "command": "/Applications/FunButton.app/Contents/MacOS/funbutton >/tmp/fb-launch.log 2>&1 & FB=$!; sleep 25; grep -iE 'embedded STT model loaded|llama-server ready|cleanup engine reloaded' /tmp/fb-launch.log; kill \"$FB\" 2>/dev/null",
      "expect_log_any": [
        "embedded STT model loaded",
        "llama-server ready",
        "cleanup engine reloaded"
      ],
      "note": "Runs the binary directly; env_logger prints to stderr at info level. On first run, models (~1.1 GB) download before these lines appear."
    }
  ],
  "docs": {
    "agent_prompt": "https://github.com/todddickerson/funbutton/blob/main/AGENT-INSTALL.md",
    "llms_txt": "https://funbutton.ai/llms.txt",
    "install_sh": "https://funbutton.ai/install.sh",
    "signing": "https://github.com/todddickerson/funbutton/blob/main/SIGNING.md"
  }
}
