catalog.json: The data source for agents, apps, tools, environments, editors, terminals and databases.Appearance.js: The plugin-owned brand colour table.scripts/check-devtools-catalog.jsaccepts it throughThemeLogic.acceptAppearancein dark and light mode.CatalogLogic.js: The pure catalog judge and the catalog's rules: a mise spec's parts and key, the spec a row installs, the specs a row declares and the machines a row builds on. It imports nothing. Callers inject the package-manager ids, Lucide icon names, brand table and package-name rule throughjudgeCatalog.bin/devtools: The engine, in node. Its header states each verb, its output and its refusals.tui/devtools.sh: The floating TUI script.install.sh,update.shandremove.shrun it with their verb. Its header states the arguments and refusals.Service.qml,Query.qml: The service and the one read-only command each of its queries runs.Window.qml,ToolRow.qml: The window and one of its rows.ViewLogic.js: Every decision the service and the window make, pure.scripts/check-devtools-catalog.js: The command-line check. It prints<rule> <path> <detail>for each refusal.scripts/test-devtools.sh: The engine's and the TUI scripts' suite, with a stub mise, pacman, sudo, uname and gum.scripts/test-devtools-view.js:ViewLogic.js's suite.scripts/smoke/rows/devtools.shis the plugin's row in the nested sandbox.
The catalog's sections, fields, install routes, mise specs and security rules are in commands-are-data.md.
Each tool is a catalog.json row with a present probe, installed through a package-manager id, a mise spec, a named installer route or container data, so the engine lists, installs and removes tools from one data file and parses no shell snippets. The PHP row installs the mise spec github:nunomaduro/static-php-builds, because bare php builds PHP from source and needs many system development packages.
The engine takes the VGS tree and a verb. Its header states each verb, its output and its refusals. The verbs are list --json, latest --json, versions, install, update, remove, use, pin, unpin, rollback, update-all and, with --mise <key>, update or remove for a tool that only the owner's global mise config declares, plus launchers refresh|remove.
bin/devtools --tree <dir> list --json
- The engine refuses the whole catalog when
CatalogLogic.judgeCatalogrefuses one row. - Install, update and remove run only in a terminal, never in a process the shell started, through
refuseOutsideTerminalinbin/lib/judge-files.js, the rule the package runner follows. The floating TUI scripttui/devtools.shis where they run. - Each mise, container and installer step runs through
bin/lib/pkg-run.shfrom$HOME, withMISE_MINIMUM_RELEASE_AGE=0and the row'sbuildEnv. Beforemise useormise up, the engine sets mise'supgrade.auto_pruneto false when it is not false already; otherwise an upgrade deletes the release a running session still executes. updateinstalls a row whosepresentprobe fails aftermise upagain withmise use -g --forceand itsbuildEnv, then runs itspostInstallsteps again, so rails and phoenix follow the new ruby and elixir.- The engine never installs a tool that only the owner's mise config declares, so that config stays the source of truth.
- A named installer downloads its upstream script to a temporary file and runs it with
sh; no shell string passes through the engine.rustupremoves itself withrustup self uninstall -y. Theopambinary that the opam installer puts in/usr/local/binstays after a removal, because only root can delete it. - A removal whose row's
presentprobe still holds is refused withpresent=remains: something VGS did not install provides it. - A database row runs the first container runtime of the row on
PATH, withoutsudo, so a user without access to that runtime sees its error in the TUI.
The service owns every command the plugin runs in the background. The window draws what the service publishes and runs nothing itself.
- The window is a Hyprland window titled Dev Tools, of class
org.vgs.shell. It opens floating and centred on the focused monitor, 600 pixels wide and 600 pixels tall, or the monitor's size less a margin a side on a smaller one, and its rows scroll inside it. Hyprland draws its border and moves, resizes, tiles and closes it like any other window. Escape closes it while it has the keyboard. Ahl.window_rule({ name = "vgs:window", enabled = false })line inhyprland.lua, after the line that loads the VGS layer, tiles the shell's windows by default. - The service runs read-only queries: the engine's
list --jsonandlatest --json,vgshell doctor --json,vgshell self status --jsonandvgshell pkg check --json --source mise. It runs them at start and on IPCrefresh. IPCopensummons the window and runs them too, but it asks the network again only when that answer is older than 10 minutes. When one of the plugin's TUI runs ends, the service lists again and asks doctor and mise again. When the core's scan finds another set of missing commands for the core or an enabled plugin, through thedoctorcapability'smissing, it lists again and asks doctor and mise again, since an install the core ran can bring mise itself. - The service publishes plugin status:
mise(its version, or Not installed),checks,installed,outdatedandmissingRequirementsfor the Settings page,catalog, the data the window draws, andlauncherRows, the rows the launcher reads. A count keeps its last answer when its query fails, andchecksreports whether a query failed. - The Catalog page shows the VGS section, then Agents, Apps, Command-line tools, Languages, Editors, Databases, Terminals and Other tools. Each row shows its icon on a brand tile, its version state, a channel Select for an install, and the actions the engine accepts. A row can choose a version, pin, unpin or roll back when mise reports the data. A row that something outside VGS provides shows Managed outside VGS and starts from
PATH. - The window opens on the Catalog search field. Tab reaches row actions in reading order, and the body scrolls each focused action into view. The initial focus never lands on Install, Update or Remove, so a summon cannot start a system change.
scripts/smoke/rows/devtools.shreads that focus state before it runs any fixture TUI. - The VGS section shows how VGS is installed and its version. When VGS is behind, Update opens the first TUI entry of group
Update, which the Updates plugin declares. The section also lists every missing requirement of VGS and of each enabled plugin. Install raises the core's requirement notice for that command through thedoctorcapability (commands-are-data.md § Consent). - Every change opens the plugin's floating TUI. The install, update and remove entries are listed in group
Dev Tools. Opened with no row, as the launcher opens them, each offers the rows its verb accepts now, from the engine'stargetsverb. Version, pin, unpin, rollback, update-all and install-launch are declared TUI entries with no launcher entry.
The manifest contributes a Dev Tools category to the launcher. The service publishes one section row and one row per catalog item while showInLauncher is true. Selecting an installed row starts it. Selecting a missing installable row opens install-launch; a successful install then starts the row. A row with no launch opens its Catalog page entry.
A terminal command is a script at ~/.local/bin/<command> that installs its row through mise on the first run and then runs it. The plugin's writeLaunchers setting, off by default because mise's shims and the owner's own agent-cli link already run each tool, asks for terminal commands. The window's switch writes it. The service runs launchers refresh while it is on and launchers remove while it is off, at start, when it changes and after each TUI run.
- A launcher's second line is
# vgs.devtools launcher. A file without that mark, a link included, belongs to the owner: the engine never replaces or deletes it, so the owner'sagent-clilinks stay the owner's. - The engine writes no launcher where the command already answers on
PATHoutside~/.local/binand mise's data directory, because the launcher would hide that copy; it reports that launchershadowed. - A row with
exec, such as Cursor, installs with an emptybin_path=, so mise puts nothing onPATH. Its launcher runs theexecfile below the directorymise wherenames. The engine writes it after each install and update whatever the setting, andlaunchers removekeeps it while the row is installed. - A launcher runs the row's default channel.