Everything you need to set up and use DevDeck
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.
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.
DevDeck uses TOML configuration at ~/.config/devdeck/devdeck.toml.
[server] port = 4242
[layout] columns = 4 landscape_columns = 6 background_color = "#000000" button_size = 60
[log] level = "info" # debug, info, warn, error, fatal file_enabled = true # Write logs to ~/.config/devdeck/logs/
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 are the core building blocks of your deck.
| 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 |
[[commands]] app = "Obsidian" icon = "doc.text" action = "open" type = "action" main = true
[[commands]] description = "Git Status" action = "git status" icon = "terminal.fill" type = "action" main = true
[[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"
Use your own PNG/JPEG images alongside SF Symbols.
[[commands]] description = "Company Deploy" icon = "custom:deploy-logo" icon_color = "#FF5733" type = "action" context = "main" main = true
Place image files in ~/.config/devdeck/icons/:
~/.config/devdeck/icons/ ├── deploy-logo.png ├── company-seal.jpg └── staging-env.png
custom:name probes name.png, name.jpg, name.jpeg automaticallyhttp://<server>/icons/<filename>Automatically switch decks based on the frontmost macOS application.
[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"]
set_manual_context WebSocket event[contexts.github] name = "GitHub" deck = "github" priority = 60 [contexts.github.match] frontmost_bundle_ids = ["com.google.Chrome"] browser_url_patterns = ["*://github.com/*"]
Multi-step automations with user input and templating.
[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
| 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.
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:
true — always submit after fillingfalse — never submitGenerate commands, contexts, and workflows from natural language using the Web UI, TUI, or mobile app.
[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
claude-sonnet-4-20250514 — requires ANTHROPIC_API_KEY env vargpt-4o — requires OPENAI_API_KEY env varqwen2.5-coder:7b — local, no API key neededWhen [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"]
DEVDECK_AI_LOG=true devdeck-server
AI generation automatically enriches prompts with real data from your Mac:
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. |
Access the built-in web interface at http://localhost:4242/app.
| 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 |
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.
The terminal UI has five tabs: Dashboard, Logs, History, Settings, and Generate.
| 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 |
| 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 |
| Key | Action |
|---|---|
↑ ↓ / j k | Move selection |
r | Refresh history |
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) |
The Chrome extension enables web app detection for context-aware switching and browser automation.
Install from the Chrome Web Store.
[contexts.github] name = "GitHub" deck = "github" priority = 60 [contexts.github.match] frontmost_bundle_ids = ["com.google.Chrome"] browser_url_patterns = ["*://github.com/*"]
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"
lsof -i :4242dns-sd -B _devdeck._tcp local.If you get "osascript is not allowed to send keystrokes" even though Accessibility permission appears enabled:
brew services restart devdeck-server