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 (includes server)
Download from GitHub Releases. The Mac app bundles the server — no Homebrew install needed. 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 = 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: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 = 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— 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-server
What 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 = 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