The desktop apps
devkit has two desktop apps: devkit.app, a menu-bar app for macOS 14 or later, and devkit-gtk, a GTK 4 tray app for Linux. They look and work the same. Both are a face on the CLI: every button runs a devkit … --json command, and one devkit watch --json keeps the window live. Whatever you do in a terminal shows up in the app within a couple of seconds, and the other way round.
The menu bar and the tray
On macOS devkit is an icon in the menu bar (a box, filled while any site runs) with no Dock icon. Clicking it opens a panel:
- Header: "devkit", a summary such as 3 sites running, and a button that opens the manager window.
- One card per stack: its name, 4 of 5 up, and a dot per service (hover for its state and role).
- Running: every running site and lane with its URL (click to open) and a stop button; then a link such as 2 stopped sites → to the manager.
- Start all (every stopped, visible site and lane, one after the other) and Stop all (lanes first, then sites).
- AI agents connected, when an agent runs
devkit mcp, with the time of its last call. It opens the MCP section. - Open devkit…, Run doctor (the full report), Quit devkit.
On Linux the same content is a tray menu (a StatusNotifierItem). Each stack is a submenu of its services; each running site is a submenu with Open, Copy URL and Stop. A left click on the icon opens the manager window. KDE Plasma, Xfce, Cinnamon, MATE, LXQt and Budgie show the tray; GNOME needs the AppIndicator extension. Without a tray, or with --no-tray, the app shows the panel as a small window instead and quits when its last window closes.
The manager window
A sidebar with seven sections, in this order: Sites, Lanes, Stacks, PHP, Templates, Settings, MCP.
- Search (Search sites) appears in Sites and Lanes and matches name, branch or URL, ignoring case and accents.
- The status bar at the bottom shows how many sites there are and how many run, Engine default: artisan · ports 8100–8999, and on the right the connected MCP agents (a link to the MCP section).
- Notices: the result of an action (a quick action's output, an error with devkit's hint) appears as a banner at the top of the section, closed with Dismiss.
- One change at a time per site: while devkit works on a site (starting, a checkout, a clone, a restore) its row shows a spinner and the entries that would change it wait. Its open, view and log entries still work.
Sites
At the top, a strip per stack with its service dots and Inbox ↗ when it has a mail catcher. Then the sites: Parked (one group per parked directory, with Clone from git… on its right) and Linked. The header has Link folder… and Park directory…, both with a folder picker.
| Column | What it shows |
|---|---|
| Site | the name (click for the site detail); lanes are indented under their site with lane 1, lane 2… |
| Branch | the git branch, an orange dot for uncommitted changes, and ahead/behind or no upstream under it |
| Engine | artisan or caddy |
| PHP | a picker for the site's version (a lane shows its site's, inherited) |
| URL | where it is served |
| Database | the database name |
| Doctor | OK, 2 warnings or 1 failure from devkit doctor <site>, run at launch and when a site appears |
| Actions | start or stop, open in the browser, the red trash (delete; for a lane: remove) and … |
The … menu
The … button, or a right-click on the row, opens the site's menu: a panel with the site's name and what it runs, then these groups.
| Group | Site | Lane |
|---|---|---|
| Open | Open in browser ⌘O, Open in editor ⌘E, Open terminal here ⌘T, Copy URL ⌘C | the same |
| Tools | New lane…, Edit .env template…, View logs, Modules, Run doctor | View logs, Modules, Run doctor |
| Quick actions | Clear log, Clear cache, Publish core assets and the site's own, each with its command as a hint | the same |
| Web | Change engine…, Xdebug… | Xdebug… |
| Database | Snapshot database, Restore snapshot… | Snapshot database, Reset database from <site>… |
| Remove | Hide from devkit (keeps files), Delete site… (deletes code) | Remove lane… (drops its database) |
The shortcuts work while the panel is open (on Linux Ctrl+O/E/T/C). On macOS the right-click menu has the same entries without shortcuts; on Linux a right-click opens the same panel.
The site detail
Click a site's name. At the top: a breadcrumb, the status, the URL and the branch bar: a branch picker (local branches, remote ones without a local copy, New branch…), the sync state (up to date with origin/main · clean), Fetch, Pull (blue Pull ↓3 when behind), Push (blue Push ↑2 when ahead) and Include submodules (on). Picking a branch does not check it out at once: a box says what will happen (the site restarts, submodules move or stay) and waits for Check out.
Below: the quick actions as one group of buttons, Open in editor, New lane… (a lane: Remove lane…) and Start site / Stop site. Then cards for the Engine (with its port), PHP (a picker; a lane shows 8.3 · follows core-development), Database, Xdebug (opens the Xdebug sheet) and Mail inbox when the stack has one.
Switching PHP from this card shows a banner: Switching to PHP 8.2, then Switched to PHP 8.2, saying that serve and the PHP processes keep the old version until the site restarts, that lanes follow the site, and any doctor problem with the new version. Restart now restarts the site and its running lanes; Undo puts the old version back. (The picker in the Sites table and in the Settings tab switch without a banner.)
Then five tabs:
- Processes
- The
serverow (php artisan serve --port=8100, orcaddy route → php-fpm) with its pid, Restart, Stop / Start and Logs; then every process with its command and port, its status (running, stopped, exited with its last lines on hover), the Autostart switch (with site), start, stop, restart and Logs. The selected log is shown below, refreshed every two seconds, with Open in logs…. - Modules and Extensions
- The submodules of
modules/andextensions/. Across the top: a Branch field, Create if missing, Checkout in all modules, Fetch, Pull all, Push all (enabled when something is ahead) and, on Extensions, Add extension…. Each row: a branch picker (the module's branch, the branch.gitmodulestracks, the site's branch, New branch…), a blue dot when it is not on the tracked branch, Tracks, Changes (clean or 3 changed), Ahead / behind, Pull, Push and Terminal. A row with uncommitted work refuses a checkout: Commit or stash first. - Database
- The database (forked from core_development for a lane), Snapshot database…, for a lane Reset from <site>…, and the snapshots (name, created, size) with Restore… and Drop….
- Settings
- Engine (Change engine…), PHP, Xdebug (Xdebug…), Database, the folder with Reveal, and the branch.
The sheets
Every change that needs a decision opens a sheet. Return presses the main button and Esc cancels, except while devkit is working. Red, destructive buttons have no Return shortcut.
| Sheet | What it asks | Defaults |
|---|---|---|
| New lane from <site> | Lane name; Branch: Create feat/<name> from a base branch, or Use an existing branch; Database: Fork with data, Schema only or Empty database; Start the lane when it is ready | Create from the current branch, Fork with data, start on |
| Clone into <directory> | Repository URL, Folder name, Branch, Clone into (with several parked directories), the steps under After cloning | every step on except Start the site when ready |
| Delete <site>? | what devkit says would go (folder, lanes, Caddy route), Drop database, Move the folder to the Trash instead of deleting it permanently, and the site's name typed to confirm. With work that exists only on this machine: Delete anyway and lose this work | Trash on, drop database off |
| Remove lane <lane>? | what lane rm --dry-run reports, Keep the database, Also delete the local branch; with unsaved work Remove anyway and discard this work | both off |
| Hide <site> from devkit? | nothing: it says the site and its lanes stop and nothing on disk changes | |
| Snapshot <database> | Name (empty: the current time); shows the existing snapshots | |
| Restore <database> | which snapshot, Snapshot the current database first; What happens lists the three steps | safety snapshot on |
| Reset <lane>'s database? | Schema and data or Schema only, Snapshot the lane database before resetting | schema and data, snapshot on |
| Drop snapshot <name>? | a confirmation: a dropped snapshot cannot be brought back | |
| Xdebug · <site> | the mode (off, debug, coverage, profile); for debug how to start a session (trigger, client host, port 9003, path mapping); which sites a container change affects | debug when off |
| Web server engine · <site> | artisan or caddy, What changes (URL, PHP, DB_HOST, REDIS_HOST, port) and Requirements (PHP installed, a free port; or Caddy and PHP services, the same-path mount, a certificate) | the other engine |
| .env template · <site> | Template / Rendered .env, Show passwords and secrets, Variables · click to insert, Save, Save and restart site | secrets hidden |
| Add to extensions · <site> | Repository URL, Folder name, Branch | the repository's default branch |
| Logs · <site> | a tab per source, Filter lines, Follow, Copy visible lines, Reveal in Finder | last 200 lines, following |
| Doctor · <site> | every check with its status and hint, Copy report, Run again |
How the apps confirm
- They ask devkit first. A destructive sheet runs the command without
--yes(or with--dry-run) and shows devkit's own answer: the uncommitted work, the database that will be dropped, the restore steps. - They ask again right before acting. If the answer changed in between (someone committed, a snapshot appeared, an agent started the site), nothing is done and the sheet says This changed since you last looked with the new answer. Any "anyway" box is cleared.
- Only then do they pass
--yesor--force.
The same applies to the default engine, the domain suffix and the port range in Settings, which go through --dry-run.
Lanes
Every lane under its site, with the same columns as Sites. New lane… at the top is a menu with one entry per site, and each site group has its own New lane….
Stacks
Add stack… picks a compose folder. Each stack is a card with Reveal, Start, Restart, Stop…, the mail Inbox ↗ and how many services are hidden by label, and a table of its services: Service, Role, Image, Ports, State and per service Restart, Stop… or Start, and Logs. Stop… warns that running sites that may use the stack's database or Redis stop working. Stopping a whole stack is docker compose down (containers removed, volumes kept); one service is docker compose stop.
PHP
Rescan and Add binary… at the top. Then:
- Default PHP: php on PATH or one of the versions, set at once.
- Host PHP: a card per binary (version, path, sites, Xdebug, extensions). The selected one's panel has Open php.ini, Copy path, Set as default, Xdebug or how to install it, Used by, Loaded extensions and
composer check-platform-reqsfor each site on it, plus Check another site. - In the same grid, a not installed card for a version a site needs, with the install command and Copy command. It is never run for you.
- Scan locations: each path with an on/off box, where it comes from (Homebrew, System, phpenv, Added…) and a remove button; Add location; Reset to defaults….
- Container PHP: the stacks' PHP-FPM services with version, image, state, the caddy sites on each and Xdebug (all sites); changing that opens a sheet naming the sites it affects.
Templates
One row per site: whether its template is stored or still seeded from .env (not stored), which lanes use it, whether it renders (every variable resolves), and Edit…, which opens the .env template sheet. Its Rendered .env tab is devkit template render --file of your draft, so you see the real result before saving.
Settings
- Default web server engine: artisan or caddy. Choosing one opens a sheet with the running sites that would stop.
- Port range: From, To, Apply…, and how many ports are in use.
- Domain suffix: caddy sites resolve as
<site>.<suffix>; Apply…. - Certificates: whether mkcert's CA is trusted and how many site certificates were issued; Trust… and Check again.
- Hosts: whether devkit's block in
/etc/hostsis current; Sync in Terminal… and Check again. - PHP binaries: a summary with Manage in PHP → and Add path….
- Doctor: the full report for the machine and every site, Copy report, Run again.
- Hidden sites: each with Show again.
- This app: devkit binary, Editor, Open devkit at login and Language, described below.
Trust… and Sync in Terminal… never run inside the app, because they ask for your password. The app asks Run devkit trust in Terminal?, writes a small self-deleting script with the app's devkit, PATH and config folder, and opens it in a terminal. When it is done, go back and press Check again.
This app
- devkit binary
- Which
devkitthe app runs, from the next launch. Empty means the one inside the app, then the first onPATH. The--devkitlaunch option andDEVKIT_BINwin over it. devkit now shows which one is in use. - Editor
- What Open in editor opens the site folder with. macOS: Default (system) (Visual Studio Code when installed, else Finder), an installed editor, or any app with Choose app…. Linux: a command, run with the folder as its argument (split as a shell would;
~and$VARare expanded in the program name); empty meanscodewhen it is onPATH, else the folder in your file manager.DEVKIT_EDITORwins over it on Linux. - Open devkit at login
- macOS adds a login item (System Settings may ask you to approve it under General → Login Items; the app offers Open Login Items). Linux writes
~/.config/autostart/vn.webpress.devkit.desktop. - Language
- System, English or Tiếng Việt, used from the next launch; Relaunch now restarts the app. System follows your preferred languages on macOS and
LANGUAGE,LC_ALL,LC_MESSAGES,LANGon Linux. On Linux the window's Menu button has the same choice. What devkit itself says (its messages, errors, hints and confirmations) stays in English, as the CLI writes it.
MCP
- Connect an agent: one pill per agent (Claude Code, Codex CLI, Gemini CLI, Cursor, VS Code · Copilot, Claude Desktop, Other (stdio)) with its terminal command or config file and Copy. For the agents configured per project (Claude Code, Gemini CLI, Cursor, VS Code) Write to project… picks a project folder and merges the entry into its config file, keeping a backup.
- Connected: each running
devkit mcpwith its agent, pid, start time and number of calls. - Tools: every tool by group, with a confirm badge on the destructive ones.
- Audit log: the latest calls (time, agent, tool, target, arguments, result), newest first, reloaded when an agent makes a call; Refresh and Reveal in Finder.
Details are in AI agents (MCP).
macOS and Linux differences
| macOS | Linux | |
|---|---|---|
| Where it lives | a menu-bar panel | a tray menu, or a panel window without a tray |
Shortcuts in the … panel | ⌘O ⌘E ⌘T ⌘C | Ctrl+O/E/T/C |
| Trust and hosts sync | open in Terminal.app | open in $TERMINAL, else the first of x-terminal-emulator, gnome-terminal, ptyxis, kgx, konsole, xfce4-terminal, alacritty, kitty, xterm |
| Open terminal here | Terminal.app | the same terminal choice |
| Folder and terminal labels | Reveal, Reveal in Finder, Sync in Terminal…, Run devkit trust in Terminal? | Show in folder, Sync in a terminal…, Run devkit trust in a terminal? |
| Editor setting | an app picker | a command; DEVKIT_EDITOR wins |
| Open at login | a login item | an XDG autostart file |
| Language setting | Settings → This app | Settings → This app, and the window's Menu |
| App settings stored in | macOS defaults (vn.webpress.devkit) | ~/.config/devkit-app/settings.json (DEVKIT_APP_CONFIG) |
| Light or dark | follows macOS | follows the desktop; DEVKIT_APP_THEME=light|dark forces one |
| PHP install commands | brew install php@X.Y | apt, dnf, pacman/AUR or Homebrew, after /etc/os-release |
.local warning | mDNS | Avahi (mDNS) |
| App log | ~/Library/Logs/devkit/devkit-app.log | ~/.local/state/devkit/devkit-app.log |
On Linux, quitting while devkit is still running a change waits for it to finish (Waiting to quit); quit again to interrupt it.
Launch options and the app log
Both apps read your login shell's PATH once at launch ($SHELL -ilc) and pass it to devkit, so docker, git, composer, pnpm and Homebrew's PHP are found as in your terminal. If the app cannot find a tool, check that it works in a new terminal window, then quit and reopen the app.
| Option | Variable | What it does |
|---|---|---|
--devkit <path> | DEVKIT_BIN | the devkit binary to run |
--config-home <dir> | DEVKIT_CONFIG_HOME | run devkit with XDG_CONFIG_HOME=<dir>: a separate set of sites and settings, handy for trying things |
--open-manager | open the manager window at launch | |
--no-tray (Linux) | use the panel window instead of the tray | |
DEVKIT_APP_LOG | where the app logs what it ran and what came back |
On macOS pass options with open /Applications/devkit.app --args --open-manager and variables with open --env NAME=value. open devkit.app --args -AppleLanguages '(vi)' starts it in Vietnamese once. The app log records command lines and their answers, with credentials in URLs removed; it is the first thing to look at when a button does not do what you expect, and the thing to attach to a support request.