Personnalisation et administrationCommandes et actions
Personnalisation et administration

Commandes et actions personnalisées

Avec cmux.json, vous apprenez à cmux vos habitudes : commandes shell fréquentes, espaces de travail aux terminaux et navigateurs déjà disposés, et actions. Vous pouvez associer ces actions à des raccourcis, à des boutons de la barre d’onglets et à la Command Palette. Placez-en un dans .cmux/cmux.json au sein d’un projet, dans ~/.config/cmux/cmux.json pour tous les projets, ou aux deux endroits.

Où cmux lit cmux.json

Les entrées peuvent provenir d’un fichier de projet, d’un fichier propre à l’utilisateur, ou des deux. Pour les fichiers de projet, cmux part du répertoire courant et vérifie aussi chaque répertoire parent au-dessus.

  • Projet : ./.cmux/cmux.json (trouvé en remontant depuis le répertoire de travail ; prioritaire sur le fichier utilisateur)
  • Repli local : ./cmux.json (toujours pris en charge pour les dépôts existants)
  • Utilisateur : ~/.config/cmux/cmux.json (partagé par tous les projets ; fournit tout ce qu’un fichier de projet laisse indéfini)
Les actions et commandes locales remplacent les entrées globales qui ont le même ID ou nom.

Modifiez cmux.json, puis appuyez sur Cmd+Shift+, ou exécutez cmux reload-config pour appliquer les changements.

Si une configuration de projet ou globale contient une erreur de schéma, cmux revient à la configuration valide suivante et affiche une ligne cmux.json Schema Error dans Command Palette. Sélectionne-la pour ouvrir le fichier de configuration.

Commandes shell

L’entrée la plus simple se compose d’un nom et d’une chaîne command. La choisir dans la Command Palette saisit cette chaîne dans le terminal qui a le focus et l’exécute.

cmux.json
{
  "commands": [
    {
      "name": "Format Changed Files",
      "description": "Run the formatter on files that differ from main",
      "keywords": [
        "fmt",
        "prettier",
        "style"
      ],
      "command": "git diff --name-only main -- '*.ts' '*.tsx' | xargs npx prettier --write"
    },
    {
      "name": "Reset Local Database",
      "keywords": [
        "db",
        "wipe"
      ],
      "command": "cd \"$(git rev-parse --show-toplevel)\" && ./scripts/db-reset.sh",
      "confirm": true
    }
  ]
}

Champs d’une commande shell

  • name: Libellé obligatoire et non vide que vous choisissez dans la Command Palette
  • command: Le texte shell que cmux saisit dans le terminal actif, puis exécute
  • description: Texte secondaire facultatif affiché à côté de l’entrée
  • keywords: Mots supplémentaires qui doivent correspondre à cette entrée lors d’une recherche dans la Command Palette
  • confirm: Mettez true pour obtenir une demande de confirmation avant chaque exécution

Une commande shell s’exécute là où se trouve le terminal actif à ce moment-là. Si la commande a besoin d’un point de départ prévisible, commencez-la par cd "$(git rev-parse --show-toplevel)" && pour aller à la racine du dépôt Git, ou par cd /some/folder && pour la fixer à un dossier précis.

Commandes qui ouvrent un espace de travail

Donnez à une commande un objet workspace au lieu d’une chaîne command, et elle ouvre alors un nouvel espace de travail, organisé en arbre de panneaux divisés contenant des terminaux et des navigateurs. Une entrée doit avoir exactement l’un des deux.

cmux.json
{
  "commands": [
    {
      "name": "API Stack",
      "keywords": [
        "api",
        "server",
        "logs"
      ],
      "restart": "confirm",
      "workspace": {
        "name": "API",
        "cwd": "./services/api",
        "color": "#10b981",
        "env": {
          "PORT": "8080"
        },
        "layout": {
          "direction": "vertical",
          "split": 0.65,
          "children": [
            {
              "direction": "horizontal",
              "children": [
                {
                  "pane": {
                    "surfaces": [
                      {
                        "type": "terminal",
                        "name": "Server",
                        "command": "go run ./cmd/api",
                        "focus": true
                      }
                    ]
                  }
                },
                {
                  "pane": {
                    "surfaces": [
                      {
                        "type": "terminal",
                        "name": "Worker",
                        "command": "go run ./cmd/worker",
                        "env": {
                          "QUEUE": "default"
                        }
                      }
                    ]
                  }
                }
              ]
            },
            {
              "pane": {
                "surfaces": [
                  {
                    "type": "terminal",
                    "name": "Logs",
                    "cwd": "/var/log",
                    "command": "tail -F system.log"
                  },
                  {
                    "type": "browser",
                    "name": "Health",
                    "url": "http://localhost:8080/healthz"
                  }
                ]
              }
            }
          ]
        }
      }
    }
  ]
}

Champs d’un objet workspace

  • name: Titre de l’espace de travail ; à défaut, le nom de la commande est utilisé
  • cwd: Répertoire de base de l’espace de travail ; le cwd de chaque onglet est résolu par rapport à lui
  • layout: Le nœud racine de l’arbre de disposition décrit ci-dessous
  • color: Couleur dans la barre latérale, en hexadécimal #RRGGBB ou sous forme de nom de couleur d’espace de travail
  • env: Environment variables inherited by every shell in the workspace
  • setup: Bootstrap command sent to the workspace's first terminal before that terminal's own command

Quand l’espace de travail est déjà ouvert

Définissez restart sur la commande pour choisir ce qui se passe lorsqu’un espace de travail du même nom existe déjà :

  • "new": Créer un nouveau workspace (par défaut)
  • "confirm": Demander d’abord, puis le remplacer si vous acceptez
  • "recreate": Le remplacer immédiatement, sans demander
  • "ignore": Basculer vers le workspace existant

Décrire une disposition

Une disposition est un arbre. Chaque nœud interne divise sa zone en deux, et chaque feuille est un panneau qui contient des onglets.

Nœuds de division

Une division partage sa zone en deux parties :

  • direction: "horizontal" ou "vertical"
  • children: Un tableau d’exactement deux nœuds, chacun étant une autre division ou un panneau
  • split: Position du séparateur, en fraction de la zone. Vaut 0.5 par défaut ; les valeurs hors de l’intervalle 0.1 à 0.9 sont ramenées dans ces bornes

Nœuds de panneau

Un panneau est une feuille de l’arbre. Son tableau surfaces liste les onglets avec lesquels il s’ouvre et doit contenir au moins une entrée.

Surfaces

Chaque entrée du tableau surfaces d’un panneau ouvre un onglet, qui est soit un terminal, soit un navigateur :

  • type: "terminal" ou "browser"
  • name: Titre d’onglet à afficher à la place du titre par défaut
  • focus: Mettez true pour donner le focus clavier à cet onglet une fois la disposition construite
  • command: Terminaux uniquement : texte shell à exécuter dès l’ouverture du terminal
  • url: Navigateurs uniquement : la page à charger
  • cwd: Répertoire de départ de cet onglet, résolu selon les règles ci-dessous
  • env: Un objet de noms et de valeurs de variables d’environnement supplémentaires

Résolution des valeurs cwd

  • ~/path: résolu à l’intérieur de votre dossier personnel
  • Un chemin qui commence par /: utilisé tel quel
  • ./subdir: ajouté au répertoire de l’espace de travail
  • . ou non défini: reprend le répertoire de l’espace de travail

Structure du fichier

Le registre d’actions est une fonctionnalité nightly. Installe la dernière build nightly avant d’utiliser actions, shortcut ou ui.surfaceTabBar.buttons.

commands définit toujours les commandes shell réutilisables et les dispositions de workspace. Les builds nightly ajoutent un registre actions. Les actions sont les ID publics partagés par la barre d’onglets de surface, Command Palette et les raccourcis d’action.

cmux.json
{
  "actions": {
    "cmux.newTerminal": {
      "type": "agent",
      "agent": "claude",
      "title": "Claude Code",
      "subtitle": "Start Claude Code in a new tab"
    },
    "lint": {
      "type": "command",
      "title": "Lint",
      "subtitle": "Run the linter in a fresh tab",
      "keywords": [
        "eslint",
        "check"
      ],
      "command": "npm run lint",
      "shortcut": [
        "cmd+k",
        "cmd+l"
      ],
      "icon": {
        "type": "symbol",
        "name": "checkmark.seal"
      }
    },
    "scratch-dir": {
      "type": "command",
      "title": "Scratch Directory",
      "command": "cd \"$(mktemp -d)\"",
      "target": "currentTerminal",
      "palette": false,
      "icon": {
        "type": "emoji",
        "value": "📝",
        "scale": 0.85
      }
    },
    "api-stack": {
      "type": "workspaceCommand",
      "title": "API Stack",
      "commandName": "API Stack"
    }
  },
  "ui": {
    "surfaceTabBar": {
      "buttons": [
        "cmux.newTerminal",
        "cmux.splitRight",
        "lint",
        {
          "action": "scratch-dir",
          "tooltip": "cd into a new temp folder"
        }
      ]
    }
  },
  "commands": [
    {
      "name": "API Stack",
      "keywords": [
        "api"
      ],
      "workspace": {
        "cwd": "./services/api",
        "layout": {
          "pane": {
            "surfaces": [
              {
                "type": "terminal",
                "name": "Server",
                "command": "go run ./cmd/api"
              }
            ]
          }
        }
      }
    }
  ]
}

Registre d’actions nightly

actions associe des ID stables à des comportements exécutables. Utilise les ID intégrés cmux.newTerminal, cmux.newBrowser, cmux.splitRight et cmux.splitDown pour remplacer les valeurs par défaut. Utilise tes propres ID pour les outils propres au projet.

palette vaut true par défaut. Mets-le à false pour masquer une action dans Command Palette tout en la gardant disponible dans la barre d’onglets de surface ou via un raccourci. shortcut utilise la même syntaxe que les raccourcis des paramètres, par exemple cmd+shift+c ou ["cmd+k", "cmd+c"].

ui.surfaceTabBar.buttons remplace la liste de boutons par défaut lorsqu’il est présent. Omettre un ID intégré permet de le masquer. Les icônes utilisent toujours une forme objet : { "type": "symbol", "name": "play.circle" }, { "type": "emoji", "value": "🧪", "scale": 0.9 } ou { "type": "image", "path": "./icons/codex.svg" }. Les chemins d’image sont relatifs au fichier de configuration. Le scale d’un emoji est facultatif et vaut 1 par défaut. SVG, PDF, PNG, JPEG, GIF, TIFF, BMP, HEIC, HEIF, WebP, AVIF, ICO et ICNS sont pris en charge.

Chaque entrée de bouton peut être une chaîne d’ID d’action ou un objet bouton. Utilise un objet bouton lorsque tu veux la même action avec un autre libellé de surface, une autre icône ou une autre infobulle. Le titre résolu du bouton sert aussi de titre à la demande d’approbation.

Place les flags d’approbation ou de permission directement dans la chaîne de commande que tu veux réellement exécuter. La cible d’action par défaut est newTabInCurrentPane, donc le schéma courant consiste à ouvrir un nouvel onglet de terminal dans le panneau actuel et à y démarrer Codex, Claude Code ou OpenCode.

Les actions locales au projet apparaissent immédiatement dans la barre d’onglets de surface et dans Command Palette. La première exécution demande toujours l’approbation. L’approbation est liée à l’empreinte exacte de l’action, pas au dépôt. Les icônes image locales au projet restent verrouillées tant que cette action n’est pas approuvée.

Actions personnalisées et Command Palette

Une entrée actions est l’élément réutilisable exécuté par cmux. Utilisez les actions quand le même comportement doit être disponible depuis Command Palette, la barre d’onglets de surface, les raccourcis ou le menu du bouton plus. Gardez commands pour les commandes shell réutilisables et les dispositions d’espace de travail. Définissez palette sur false quand une action ne doit pas apparaître dans Command Palette.

Types d’action

  • "command": Exécute du texte shell dans un terminal. Utilisez target pour choisir le terminal courant ou un nouvel onglet dans le panneau courant.
  • "agent": Lance un agent de codage pris en charge. Aujourd’hui, codex et claude sont pris en charge, avec des arguments optionnels.
  • "builtin": Crée un alias vers une action cmux intégrée comme cmux.newTerminal, cmux.newBrowser, cmux.splitRight ou cmux.splitDown. Actions intégrées de copie : cmux.copyWorkingDirectory copie le répertoire de travail du terminal, cmux.copyProjectRoot copie la racine de son dépôt git (ou le répertoire de travail hors d'un dépôt) et cmux.copyScreen copie l'écran visible. Dans un espace de travail SSH, cloud ou tmux distant, cmux.copyProjectRoot copie le répertoire de travail distant, car cmux ne peut pas consulter le dépôt git sur l'autre hôte.
  • "workspaceCommand": Exécute une définition d’espace de travail nommée depuis commands. Utilisez-le pour les dispositions multi-panneaux, les dossiers de travail personnalisés et les commandes de démarrage.
  • "workspace": Create a workspace from an inline workspace definition, with optional restart behavior. Offered in the new-workspace plus-button menu automatically.
  • "setting": Modifie un réglage dans votre cmux.json global, par exemple pour basculer un réglage true/false ou parcourir une liste de valeurs. Voir Actions de réglage ci-dessous.
  • "settingPreset": Applique en une seule fois un groupe nommé de réglages défini dans settingPresets.

Champs d’action

  • title: Titre de ligne dans Command Palette, libellé du bouton, titre de l’élément de menu et titre de l’invite de confiance, sauf si une entrée le remplace.
  • subtitle / description: Texte secondaire dans Command Palette. description est accepté comme alias de subtitle.
  • keywords: Termes de recherche supplémentaires pour Command Palette.
  • palette: Vaut true par défaut pour les actions personnalisées. Définissez false pour masquer l’action dans Command Palette tout en la laissant appelable ailleurs.
  • shortcut: Raccourci d’action optionnel, avec la même syntaxe de touche unique ou d’accord en deux temps que les raccourcis des réglages.
  • target: Pour les actions command et agent uniquement. Utilisez currentTerminal ou newTabInCurrentPane.
  • confirm: Demande confirmation avant d’exécuter l’action.
  • newWorkspaceMenu: Set true to offer any action in the new-workspace plus-button menu, or false to hide a workspace action from it.

Comportement de Command Palette

Command Palette lit le registre d’actions résolu. Les ID d’actions personnalisées sont ajoutés comme lignes quand palette n’est pas false. Les anciens commands sont ajoutés automatiquement comme lignes personnalisées, sauf si une action avec le même ID généré existe déjà. Les commandes intégrées gardent leurs libellés habituels, mais remplacer un ID intégré comme cmux.newTerminal change le comportement derrière ce point d’entrée partagé.

Actions de réglage

Une action de réglage indique le path d’un réglage et exactement un des champs suivants : set (écrire une valeur), toggle: true (inverser un réglage true/false), cycle (passer à la valeur suivante d’une liste, en revenant à la première après la dernière) ou unset: true (supprimer la clé de cmux.json pour que la valeur de la fenêtre Réglages ou la valeur par défaut s’applique). toggle et cycle partent de la valeur utilisée par cmux, y compris celle que vous avez définie dans la fenêtre Réglages. Une action settingPreset applique une entrée de settingPresets, un cmux.json partiel dont les objets imbriqués sont fusionnés clé par clé. cmux vérifie chaque modification par rapport au schéma avant de l’enregistrer, conserve vos commentaires et les autres clés, et l’applique immédiatement. Les actions de réglage et les presets ne s’exécutent que depuis votre cmux.json global et les packs qu’il référence ; les configurations de projet ne peuvent pas les déclarer.

~/.config/cmux/cmux.json
{
  "actions": {
    "scroll.cycle": {
      "type": "setting",
      "title": "Cycle Scroll Speed",
      "path": "terminal.scrollSpeed",
      "cycle": [1.0, 1.4, 1.8]
    },
    "editor.wrap": {
      "type": "setting",
      "title": "Toggle Editor Word Wrap",
      "path": "fileEditor.wordWrap",
      "toggle": true
    },
    "sidebar.quiet": {
      "type": "settingPreset",
      "title": "Quiet Sidebar",
      "preset": "sidebar.quiet"
    }
  },
  "settingPresets": {
    "sidebar.quiet": {
      "sidebar": { "showPorts": false, "showPullRequests": false, "showLog": false }
    }
  }
}

Les mêmes modifications fonctionnent depuis un terminal : cmux config set terminal.scrollSpeed 1.4, cmux config toggle fileEditor.wordWrap et cmux config preset sidebar.quiet.

Avec confirm: true, cmux demande confirmation avant d’enregistrer et affiche la commande cmux config équivalente. Les chemins sont découpés à chaque ".", donc une clé qui contient elle-même "." (comme une entrée workspaceGroups.byCwd pour ~/src/app.web) ne peut pas être modifiée ainsi. Modifiez-la directement dans cmux.json.

Actions personnalisées du bouton plus

Utilise ui.newWorkspace.action pour remplacer ce que fait le bouton plus. Utilise ui.newWorkspace.contextMenu (ou l’alias rightClick) pour définir le menu contextuel ordonné. Les entrées de menu peuvent être des ID d’action, des objets d’action ou { "type": "separator" }. Sans configuration, le menu affiche cmux.newWorkspace (⌘N), cmux.newCloudWorkspace (⌘Y), cmux.newCloudMachine (⇧⌘Y), cmux.newTerminal (⌘T), cmux.newBrowser (⇧⌘L), chacun avec son raccourci actuel. Les entrées cloud apparaissent uniquement lorsque Cloud Machines est activé. New Cloud Workspace utilise la machine du dernier espace de travail Cloud sélectionné, puis la première de la barre latérale droite ; si aucune machine utilisable n’existe, ouvre New Cloud Machine.

cmux.json
{
  "actions": {
    "worktree-agents": {
      "type": "workspaceCommand",
      "title": "Agents in New Worktree",
      "commandName": "Agents in New Worktree",
      "icon": {
        "type": "symbol",
        "name": "arrow.triangle.branch"
      }
    }
  },
  "ui": {
    "newWorkspace": {
      "action": "worktree-agents",
      "contextMenu": [
        "worktree-agents",
        {
          "type": "separator"
        },
        {
          "action": "cmux.newWorkspace",
          "title": "Blank Workspace"
        },
        {
          "action": "cmux.newBrowser",
          "title": "Browser"
        }
      ]
    }
  },
  "commands": [
    {
      "name": "Agents in New Worktree",
      "description": "Branch off a new Git worktree, then run Codex and Claude Code in it side by side",
      "workspace": {
        "name": "Worktree",
        "layout": {
          "direction": "vertical",
          "split": 0.7,
          "children": [
            {
              "direction": "horizontal",
              "children": [
                {
                  "pane": {
                    "surfaces": [
                      {
                        "type": "terminal",
                        "name": "Codex",
                        "command": "marker=\"${TMPDIR:-/tmp}/cmux-wt-${CMUX_WORKSPACE_ID:-local}\"; echo \"Waiting for the worktree...\"; until [ -s \"$marker\" ]; do sleep 0.25; done; cd \"$(cat \"$marker\")\" && exec codex --yolo"
                      }
                    ]
                  }
                },
                {
                  "pane": {
                    "surfaces": [
                      {
                        "type": "terminal",
                        "name": "Claude",
                        "command": "marker=\"${TMPDIR:-/tmp}/cmux-wt-${CMUX_WORKSPACE_ID:-local}\"; echo \"Waiting for the worktree...\"; until [ -s \"$marker\" ]; do sleep 0.25; done; cd \"$(cat \"$marker\")\" && exec claude --dangerously-skip-permissions"
                      }
                    ]
                  }
                }
              ]
            },
            {
              "pane": {
                "surfaces": [
                  {
                    "type": "terminal",
                    "name": "Setup",
                    "command": "marker=\"${TMPDIR:-/tmp}/cmux-wt-${CMUX_WORKSPACE_ID:-local}\"; rm -f \"$marker\"; root=$(git rev-parse --show-toplevel) || exit 1; branch=\"wt-$(date +%m%d-%H%M%S)\"; wt=\"$root/../$(basename \"$root\")-$branch\"; git -C \"$root\" worktree add -b \"$branch\" \"$wt\" && printf \"%s\\n\" \"$wt\" > \"$marker\" && cd \"$wt\" && exec \"${SHELL:-/bin/zsh}\" -l",
                    "focus": true
                  }
                ]
              }
            }
          ]
        }
      }
    }
  ]
}

Cet exemple fait exécuter l’action worktree-agents par le clic normal sur le bouton plus. La commande d’espace de travail de commands utilise un terminal de préparation visible pour créer d’abord le worktree Git. Codex et Claude démarrent en même temps, attendent le fichier d’état propre à l’espace de travail, puis se placent dans le dossier créé avant exec.

Workspace layouts

A workspace layout is stored as a workspace action, embedding a full workspace definition directly in the action so it needs no separate commands entry. It supports the same workspace fields plus setup, a bootstrap command sent to the first terminal before that terminal's own command:

cmux.json
{
  "actions": {
    "pair-review": {
      "type": "workspace",
      "title": "Pair Review",
      "icon": {
        "type": "symbol",
        "name": "person.2.wave.2"
      },
      "restart": "ignore",
      "newWorkspaceMenu": true,
      "workspace": {
        "name": "Review",
        "cwd": "~/src/app",
        "setup": "git pull --ff-only",
        "layout": {
          "direction": "horizontal",
          "split": 0.55,
          "children": [
            {
              "pane": {
                "surfaces": [
                  {
                    "type": "terminal",
                    "name": "Diff",
                    "command": "git log -p -n 5",
                    "focus": true
                  }
                ]
              }
            },
            {
              "pane": {
                "surfaces": [
                  {
                    "type": "terminal",
                    "name": "Codex",
                    "command": "codex"
                  },
                  {
                    "type": "terminal",
                    "name": "Shell"
                  }
                ]
              }
            }
          ]
        }
      }
    }
  }
}

Workspace layouts appear in the plus-button right-click menu automatically. Set newWorkspaceMenu to false to hide one, or to true on any other action to add it to the menu.

You can also create these without writing JSON: right-click the plus button and choose Save Workspace as Layout to capture the current workspace's splits, directories, running agents, and browser tabs into ~/.config/cmux/cmux.json. They are stored as entries in the actions block. Customize Workspace Layouts opens that file in your editor.

Default for new workspaces

Use the Default for New Workspace submenu to pick which saved workspace layout plain New Workspace should run, or choose None to return to a blank terminal. The save dialog also has a Use as default for new workspaces checkbox. The setting is ui.newWorkspace.action; a project-local .cmux/cmux.json value wins over ~/.config/cmux/cmux.json.

Exemple complet

cmux.json
{
  "actions": {
    "cmux.newTerminal": {
      "type": "command",
      "title": "Codex",
      "command": "codex --yolo",
      "shortcut": "cmd+t",
      "icon": {
        "type": "image",
        "path": "./icons/codex.svg"
      }
    },
    "review": {
      "type": "agent",
      "agent": "claude",
      "args": "--permission-mode plan",
      "title": "Plan with Claude",
      "shortcut": "cmd+shift+p",
      "icon": {
        "type": "symbol",
        "name": "list.bullet.clipboard"
      }
    },
    "storybook": {
      "type": "workspaceCommand",
      "commandName": "Storybook",
      "newWorkspaceMenu": true
    },
    "typecheck": {
      "type": "command",
      "title": "Typecheck",
      "command": "npx tsc --noEmit",
      "target": "newTabInCurrentPane",
      "confirm": false,
      "icon": {
        "type": "emoji",
        "value": "🔎"
      }
    }
  },
  "ui": {
    "surfaceTabBar": {
      "buttons": [
        "cmux.newTerminal",
        "review",
        {
          "action": "typecheck",
          "title": "tsc"
        },
        "cmux.newBrowser",
        "cmux.splitDown"
      ]
    },
    "newWorkspace": {
      "contextMenu": [
        "storybook",
        {
          "type": "separator"
        },
        "cmux.newTerminal",
        "cmux.newBrowser"
      ]
    }
  },
  "commands": [
    {
      "name": "Storybook",
      "description": "Component explorer with a live preview beside it",
      "keywords": [
        "ui",
        "components",
        "stories"
      ],
      "workspace": {
        "name": "Storybook",
        "cwd": "./packages/ui",
        "color": "#a855f7",
        "layout": {
          "direction": "horizontal",
          "split": 0.4,
          "children": [
            {
              "direction": "vertical",
              "split": 0.5,
              "children": [
                {
                  "pane": {
                    "surfaces": [
                      {
                        "type": "terminal",
                        "name": "Storybook",
                        "command": "npx storybook dev -p 6006 --no-open",
                        "focus": true
                      }
                    ]
                  }
                },
                {
                  "pane": {
                    "surfaces": [
                      {
                        "type": "terminal",
                        "name": "Tests",
                        "command": "npx vitest --watch",
                        "env": {
                          "CI": "false"
                        }
                      }
                    ]
                  }
                }
              ]
            },
            {
              "pane": {
                "surfaces": [
                  {
                    "type": "browser",
                    "name": "Preview",
                    "url": "http://localhost:6006"
                  }
                ]
              }
            }
          ]
        }
      }
    },
    {
      "name": "Install Dependencies",
      "keywords": [
        "install",
        "deps"
      ],
      "command": "npm ci",
      "confirm": true
    },
    {
      "name": "Open Changelog",
      "keywords": [
        "changes",
        "release"
      ],
      "command": "${EDITOR:-vi} CHANGELOG.md"
    }
  ]
}