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-serverStart manually:
devdeck-serverOr as a background service:
brew services start devdeck-serverServer starts on port 4242 by default.
Option B: Mac App (includes server)
Install via Homebrew:
brew tap devdeck-app/homebrew-devdeck-server
brew install --cask devdeckOr download the DMG directly from GitHub Releases. The Mac app bundles the server — no brew services needed; just launch the app. Adds a menu bar icon, QR code pairing, and server controls.
Connecting the App
- Install DevDeck from the App Store on your iPhone/iPad
- Start the server using either the CLI or the Mac app
- Ensure your phone and Mac are on the same Wi-Fi network
- The app auto-discovers the server via mDNS — or scan the QR code if using the Mac app
Note:Pick Option A or B — not both. The CLI server is headless; your phone discovers it automatically via mDNS. The Mac app adds GUI conveniences but is not required.
Configuration
DevDeck uses TOML configuration at ~/.config/devdeck/devdeck.toml.
Server
[server]
port = 4242Layout
[layout]
columns = 4
landscape_columns = 6
background_color = "#000000"
button_size = 60Logging
[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 = trueRunning a Shell Command
[[commands]]
description = "Git Status"
action = "git status"
icon = "terminal.fill"
type = "action"
main = trueCreating 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 = trueIcon Directory
Place image files in ~/.config/devdeck/icons/:
~/.config/devdeck/icons/
├── deploy-logo.png
├── company-seal.jpg
└── staging-env.pngRules
custom:nameprobesname.png,name.jpg,name.jpegautomatically- 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_contextWebSocket 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 = trueStep 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 fillingfalse— 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 | cliProvider Defaults
- Anthropic:
claude-sonnet-4-20250514— requiresANTHROPIC_API_KEYenv var - OpenAI:
gpt-4o— requiresOPENAI_API_KEYenv 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-serverWhat AI Can Generate
- Commands— single actions (app launches, shell commands) with appropriate icons
- Contexts— auto-switching rules with bundle IDs, priorities, and a set of commands
- 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 |
|---|---|
1–5 | Jump to tab |
Tab / Shift+Tab | Cycle tabs |
? | Toggle help overlay |
o | Open web UI in browser |
q / Ctrl+C | Quit |
Logs Tab
| Key | Action |
|---|---|
a | Toggle auto-scroll |
d / i / w / e | Filter by DEBUG / INFO / WARN / ERROR |
c | Clear filter (show all) |
x | Clear all logs |
↑ ↓ / j k | Scroll |
History Tab
| Key | Action |
|---|---|
↑ ↓ / j k | Move selection |
r | Refresh history |
Generate Tab
Flow: type prompt → preview result → edit fields → save to config
| Key | Action |
|---|---|
Enter / / | Focus input |
Enter (focused) | Submit prompt |
s | Save generated command/context/workflow |
e | Toggle edit mode |
r | Regenerate with same prompt |
Esc | Cancel / discard preview / unfocus input |
Tab / Shift+Tab | Cycle 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 = trueWhen 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 → removeDevDeck (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