Documentation

Everything you need to set up and use DevDeck

Getting Started

Option A: Homebrew (CLI)

brew tap devdeck-app/homebrew-devdeck-server
brew install --cask devdeck-server

Start manually:

devdeck-server

Or as a background service:

brew services start devdeck-server

Server starts on port 4242 by default.

Option B: Mac App

Download the native macOS menu bar app from GitHub Releases. Includes QR code pairing, server status, and quick actions.

The Mac app runs the same server — no separate CLI install needed.

Connecting the App

  1. Install DevDeck from the App Store on your iPhone/iPad
  2. Ensure your phone and Mac are on the same Wi-Fi network
  3. The app auto-discovers the server via mDNS — or scan the QR code shown in the server's menu bar

Configuration

DevDeck uses TOML configuration at ~/.config/devdeck/devdeck.toml.

Server

[server]
port = 4242

Layout

[layout]
columns           = 4
landscape_columns = 6
background_color  = "#000000"
button_size       = 60

Logging

[log]
level        = "info"  # debug, info, warn, error, fatal
file_enabled = true  # Write logs to ~/.config/devdeck/logs/

History

Command execution history is stored in ~/.config/devdeck/devdeck.db. By default, only metadata (success, exit code, duration) is stored — command output is omitted for security.

[history]
store_output = true  # Enable storing command output (use with caution)

Commands

Commands are the core building blocks of your deck.

Command Fields

Field Type Description
uuid string Optional; auto-generated if not provided
description string Button label
app string Application name (for open action)
action string Shell command to execute
icon string SF Symbol name (e.g., terminal.fill, gear)
icon_color string Hex color for SF Symbol tinting
type string action or context
context string main or custom context name
main bool Show on main screen

Opening an Application

[[commands]]
app    = "Obsidian"
icon   = "doc.text"
action = "open"
type   = "action"
main   = true

Running a Shell Command

[[commands]]
description = "Git Status"
action      = "git status"
icon        = "terminal.fill"
type        = "action"
main        = true

Creating a Context with Sub-commands

[[commands]]
app     = "System Preferences"
type    = "context"
icon    = "gearshape.fill"
context = "system"
main    = true

[[commands]]
description = "Volume Down"
action      = "aerospace volume down"
type        = "action"
context     = "system"

[[commands]]
description = "Volume Up"
action      = "aerospace volume up"
type        = "action"
context     = "system"

Custom Icons

Use your own PNG/JPEG images alongside SF Symbols.

Usage

[[commands]]
description = "Company Deploy"
icon        = "custom:deploy-logo"
icon_color  = "#FF5733"
type        = "action"
context     = "main"
main        = true

Icon Directory

Place image files in ~/.config/devdeck/icons/:

~/.config/devdeck/icons/
├── deploy-logo.png
├── company-seal.jpg
└── staging-env.png

Rules

  • custom:name probes name.png, name.jpg, name.jpeg automatically
  • Allowed formats: PNG, JPEG only (max 512KB)
  • Icons are served via http://<server>/icons/<filename>
  • Changes to the icons directory trigger automatic reload

Context-Aware Deck Switching

Automatically switch decks based on the frontmost macOS application.

Configuration

[settings]
auto_context_enabled = true
auto_context_poll_ms = 500

[contexts.default]
name     = "Default"
deck     = "main"
priority = 0

[contexts.slack]
name     = "Slack"
deck     = "slack"
priority = 50
[contexts.slack.match]
frontmost_bundle_ids = ["com.tinyspeck.slackmacgap"]
frontmost_app_names  = ["Slack"]

How It Works

  • Priority-based matching — highest priority context wins
  • Manual override available via the app or set_manual_context WebSocket event
  • Detection uses a native Swift binary for reliable frontmost app detection
  • Browser URL patterns supported when the Chrome extension is connected

Browser-Aware Context Example

[contexts.github]
name     = "GitHub"
deck     = "github"
priority = 60
[contexts.github.match]
frontmost_bundle_ids = ["com.google.Chrome"]
browser_url_patterns = ["*://github.com/*"]

Workflows

Multi-step automations with user input and templating.

Basic Workflow

[workflows.search-docs]
name        = "Search Docs"
description = "Search documentation sites"
icon        = "magnifyingglass"
inputs      = ["query"]

[[workflows.search-docs.steps]]
type = "open_url"
url  = "https://docs.example.com/search?q={{query}}"

[[workflows.search-docs.steps]]
type              = "run_command"
command           = "echo 'Opened docs for {{query}}'"
timeout           = 10
continue_on_error = true

Step Types

Type Description
open_url Opens URL in browser. Routes through extension when connected.
run_command Executes shell command (default 30s timeout)
delay Pauses execution (seconds)
click_element Clicks DOM element by CSS selector (requires extension)
fill_input Fills single input by CSS selector (requires extension)
fill_form Fills form fields by semantic name (requires extension)

Template variables use {{inputName}} syntax.

Form Filling (fill_form)

Fills web forms using semantic field names — no CSS selectors needed.

# Single-field: auto-submits by default
[workflows.ask-gemini]
name   = "Ask Gemini"
icon   = "sparkles"
inputs = ["query"]

[[workflows.ask-gemini.steps]]
type = "open_url"
url  = "https://gemini.google.com/"

[[workflows.ask-gemini.steps]]
type = "fill_form"
[workflows.ask-gemini.steps.fields]
prompt = "{{query}}"

auto_submit behavior:

  • Not set — smart default: auto-submit single-field forms, skip for multi-field
  • true — always submit after filling
  • false — never submit

AI Generation

Generate commands, contexts, and workflows from natural language using the Web UI, TUI, or mobile app.

Provider Configuration

[ai]
provider   = "anthropic"  # "anthropic" (default), "openai", or "ollama"
model      = ""           # Optional; uses provider default if omitted
form_model = ""           # Optional; cheaper model for form field analysis
host       = ""           # Ollama only (default: http://localhost:11434)

# Method preferences (optional, defaults to "auto")
preferred_url_method    = "auto"  # auto | extension | open | chrome-cli
preferred_action_method = "auto"  # auto | shortcut | applescript | cli

Provider Defaults

  • Anthropic: claude-sonnet-4-20250514 — requires ANTHROPIC_API_KEY env var
  • OpenAI: gpt-4o — requires OPENAI_API_KEY env var
  • Ollama: qwen2.5-coder:7b — local, no API key needed

Auto-Injected Button

When [ai] is configured, an "AI Command" workflow button automatically appears on your main deck. Tap it to generate commands, contexts, or workflows from natural language prompts.

Customize the button by defining a workflow with the key generate_command:

[workflows.generate_command]
name   = "My AI"
icon   = "sparkles"
inputs = ["prompt"]

Debug AI Generation

DEVDECK_AI_LOG=true devdeck-server

What AI Can Generate

  1. Commands — single actions (app launches, shell commands) with appropriate icons
  2. Contexts — auto-switching rules with bundle IDs, priorities, and a set of commands
  3. Workflows — multi-step automations with inputs and templating

Environment-Aware Generation

AI generation automatically enriches prompts with real data from your Mac:

  • Menu bar scraping — reads real keyboard shortcuts from the frontmost app's menus (e.g., ⌘N for "New Window" in Finder) so generated commands use correct shortcuts instead of guessing
  • Capability detection — probes for browser extension, chrome-cli, and other tools to tailor commands to what's actually available on your system
  • Web search fallback — when menu data isn't available (Anthropic and OpenAI only), the AI searches the web for accurate shortcuts and app details

Method Preferences

Control how generated commands interact with your system:

Setting Options Description
preferred_url_method auto, extension, open, chrome-cli How to open URLs. Auto picks the best available method.
preferred_action_method auto, shortcut, applescript, cli How to trigger app actions. Auto prefers keyboard shortcuts.

Web UI

Access the built-in web interface at http://localhost:4242/app.

Two Tabs

Tab Description
Generate Type a natural language prompt → preview the AI-generated result → edit fields → save to your deck
Settings Configure logging, AI provider, layout, proxy, context detection, history, and server options

Deck Simulator

The Generate tab includes a live grid preview of your deck layout that shows ghost buttons for unsaved commands before you commit them.

Localhost-only, no authentication required.

Terminal UI

The terminal UI has five tabs: Dashboard, Logs, History, Settings, and Generate.

Global Shortcuts

Key Action
15Jump to tab
Tab / Shift+TabCycle tabs
?Toggle help overlay
oOpen web UI in browser
q / Ctrl+CQuit

Logs Tab

Key Action
aToggle auto-scroll
d / i / w / eFilter by DEBUG / INFO / WARN / ERROR
cClear filter (show all)
xClear all logs
/ j kScroll

History Tab

Key Action
/ j kMove selection
rRefresh history

Generate Tab

Flow: type prompt → preview result → edit fields → save to config

Key Action
Enter / /Focus input
Enter (focused)Submit prompt
sSave generated command/context/workflow
eToggle edit mode
rRegenerate with same prompt
EscCancel / discard preview / unfocus input
Tab / Shift+TabCycle editable fields (in edit mode)

Browser Extension

The Chrome extension enables web app detection for context-aware switching and browser automation.

Installation

Install from the Chrome Web Store.

What It Does

  • Detects current tab URL and title for context-aware deck switching
  • Supports browser automation: clicking elements, filling forms, extracting data
  • Optional auth token for secured connections
  • Auto-reconnects with exponential backoff

Browser-Aware Context Example

[contexts.github]
name     = "GitHub"
deck     = "github"
priority = 60
[contexts.github.match]
frontmost_bundle_ids = ["com.google.Chrome"]
browser_url_patterns = ["*://github.com/*"]

Proxy & Remote Access

Enable automatic ngrok tunnel detection for remote access:

[proxy]
enable = true

When enabled, the server checks for a running ngrok tunnel and starts one if not found. The QR code and connection URL will use the ngrok public URL automatically.

Alternatively, set a manual external URL:

[settings]
manual_url = "example.ngrok-free.dev"

Troubleshooting

Server Not Found

  • Verify server is running: lsof -i :4242
  • Test mDNS: dns-sd -B _devdeck._tcp local.
  • Ensure phone and Mac are on the same Wi-Fi network
  • Check macOS firewall isn't blocking port 4242

Connection Drops

  • Verify auth token matches between app and server
  • Check server logs for authentication errors
  • Try manual connection with QR code

Camera/QR Not Working

  • Check camera permissions: Settings → DevDeck → Camera
  • QR scanner requires a physical device (not simulator)

Keystroke Commands Not Working

If you get "osascript is not allowed to send keystrokes" even though Accessibility permission appears enabled:

  • Stale TCC entry after rebuild: macOS keys Accessibility permissions by code signature. Rebuilding the app changes the signature, silently invalidating the old permission — even though System Settings still shows it as "on"
  • Fix: System Settings → Privacy & Security → Accessibility → remove DevDeck (click −), then re-add it. Toggling off/on is not enough
  • Also check Automation: DevDeck needs a separate entry under Privacy → Automation allowing control of System Events

Config Changes Not Applying

  • Config hot-reloads automatically — changes should apply within seconds
  • Check for TOML syntax errors in server logs
  • Restart server if issues persist: brew services restart devdeck-server